freesolo-flash-dev 1.0.87__tar.gz → 1.0.89__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.87 → freesolo_flash_dev-1.0.89}/Dockerfile.worker +46 -7
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/PKG-INFO +43 -1
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/README.md +42 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/catalog.py +2 -1
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/TRAINING.md +6 -9
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/__init__.py +17 -15
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/commands.py +215 -23
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/env_eval.py +288 -161
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/env_setup.py +8 -4
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/render.py +16 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/client/http.py +42 -10
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cost/analytical.py +333 -11
- freesolo_flash_dev-1.0.89/flash/cost/spec.py +248 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cost/types.py +20 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/accounting.py +2 -0
- freesolo_flash_dev-1.0.89/flash/engine/rollout_profile.py +253 -0
- freesolo_flash_dev-1.0.89/flash/engine/sft_workload.py +442 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/__init__.py +40 -7
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/backend_common.py +270 -6
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/heartbeat.py +1 -1
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/lora.py +0 -139
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/opd_train.py +44 -7
- freesolo_flash_dev-1.0.89/flash/engine/worker/packing.py +192 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/perf/__init__.py +0 -2
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/perf/liger.py +0 -16
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/perf/loraplus.py +2 -1
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/rl_train.py +139 -53
- freesolo_flash_dev-1.0.89/flash/engine/worker/rng.py +41 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/sft.py +4 -4
- freesolo_flash_dev-1.0.89/flash/engine/worker/sft_profile.py +63 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/sft_train.py +117 -269
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/wandb_log.py +1 -1
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/evaluations.py +1 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/allocator.py +39 -8
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/train/endpoints.py +5 -1
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/runner/__init__.py +309 -29
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/runner/deploy.py +16 -1
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/runner/lifecycle.py +10 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/schema/__init__.py +6 -6
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/db.py +72 -2
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/routes/runs.py +70 -1
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/spec.py +22 -0
- freesolo_flash_dev-1.0.89/flash/workload_profile.py +652 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/pyproject.toml +2 -2
- freesolo_flash_dev-1.0.89/tests/_helpers/profile.py +197 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/conftest.py +8 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_backend_common.py +177 -14
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cancel_remote.py +51 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_charge_pricing.py +44 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_commands.py +5 -1
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_credential_shadowing.py +4 -4
- freesolo_flash_dev-1.0.89/tests/test_cli_estimate.py +743 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_help.py +5 -4
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_client.py +43 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_controlled_experiment_repairs.py +18 -5
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cost_estimate.py +50 -9
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_disk_gb.py +5 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_evaluations.py +623 -352
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_flash_worker.py +19 -10
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_grpo_reward_batching.py +66 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_managed_hf_repo.py +4 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_multi_gpu_gate.py +19 -7
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_multimodal_training.py +47 -18
- freesolo_flash_dev-1.0.89/tests/test_packing.py +199 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_packing_coverage.py +0 -6
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_provider_routing.py +142 -26
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_repo_cleanup.py +13 -1
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_resume_on_retry.py +11 -1
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_reward_profile.py +5 -1
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_rl_train.py +32 -2
- freesolo_flash_dev-1.0.89/tests/test_rollout_profile_quote.py +110 -0
- freesolo_flash_dev-1.0.89/tests/test_rollout_profile_sampling.py +278 -0
- freesolo_flash_dev-1.0.89/tests/test_rollout_workload_profile.py +500 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_runmgmt.py +92 -56
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_api.py +191 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_billing.py +131 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_db.py +129 -0
- freesolo_flash_dev-1.0.89/tests/test_sft_max_context.py +97 -0
- freesolo_flash_dev-1.0.89/tests/test_sft_profile_worker.py +244 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_sft_train.py +173 -2
- freesolo_flash_dev-1.0.89/tests/test_sft_workload.py +234 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_spec_and_validation.py +20 -9
- freesolo_flash_dev-1.0.89/tests/test_step_floor.py +222 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_training_controls.py +35 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_weight_cache.py +4 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_worker_stack.py +108 -16
- freesolo_flash_dev-1.0.89/tests/test_workload_profile.py +246 -0
- freesolo_flash_dev-1.0.89/tests/test_workload_profile_lifecycle.py +394 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/uv.lock +1 -1
- freesolo_flash_dev-1.0.87/flash/cost/spec.py +0 -183
- freesolo_flash_dev-1.0.87/flash/engine/worker/packing.py +0 -377
- freesolo_flash_dev-1.0.87/flash/engine/worker/rng.py +0 -29
- freesolo_flash_dev-1.0.87/tests/test_cli_estimate.py +0 -427
- freesolo_flash_dev-1.0.87/tests/test_grpo_mask_aware.py +0 -190
- freesolo_flash_dev-1.0.87/tests/test_packing.py +0 -922
- freesolo_flash_dev-1.0.87/tests/test_sft_max_context.py +0 -60
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.dockerignore +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.env.example +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/dependabot.yml +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/auto-rebake.yml +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/bake-kernel-cache.yml +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/ci.yml +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/main-source-guard.yml +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/notify-tests-repo.yml +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/publish-dev.yml +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/publish-image.yml +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/publish.yml +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/version-parity.yml +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.github/workflows/worker-image.yml +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/.gitignore +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/CODE_OF_CONDUCT.md +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/CONTRIBUTING.md +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/Dockerfile +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/LICENSE +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/NOTICE +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/SECURITY.md +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/SELF_HOSTING.md +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/build/kernel_cache/.gitignore +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/build/kernel_cache/.keep +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/deploy/infisical/Dockerfile +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/deploy/infisical/README.md +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/deploy/infisical/entrypoint.sh +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/docker/Dockerfile.kernelcache +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/docker/Dockerfile.kernelcache.relayer +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/docker/bake_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/docker/bake_pod_entry.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/docker/kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/docker/make_rp_handler.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/_channel.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/_fileio.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/_logging.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/_paths.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/adapter_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/__main__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/_tty.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/env_test.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/envpush.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/traces.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cli/training_doc.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/client/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/client/config.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/client/runtime_secrets.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/client/specs.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cost/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/cost/facts.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/multiturn_reward_scoring.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/recipe.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/reward_profile.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/steps.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/vram.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/__main__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/_pkg.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/adapter.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/decoding.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/finalize.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/grpo.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/grpo_multiturn.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/grpo_plugin.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/hf.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/kernel_warmup.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/multiturn_glue.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/opd.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/opd_gkd.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/opd_multiturn.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/opd_plugin.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/opd_structured.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/perf/attn.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/perf/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/perf/lifecycle.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/perf/memory.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/rl.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/rollout_samples.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/teacher.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/teacher_encoding.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker/tokenizer_align.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/engine/worker_entrypoint.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/adapter.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/archive.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/archive_policy.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/base.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/loader.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/pull.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/envs/registry.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/lora_rank.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/multimodal.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/opd_limits.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/opd_retry_contract.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/opd_validation.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_auth.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_deadline.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_hf_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_http.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_instance.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_instance_bootstrap.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_instance_poll.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_instance_provider.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_poll.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/_worker.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/base.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/lambdalabs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/lambdalabs/api.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/lambdalabs/auth.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/lambdalabs/gpus.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/lambdalabs/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/lambdalabs/preflight.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/lambdalabs/pricing.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/preflight.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/realized.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/api.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/auth.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/cost.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/gpus.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/jobs.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/keys.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/preflight.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/pricing.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/slots.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/runpod/train/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/vast/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/vast/api.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/vast/auth.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/vast/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/vast/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/vast/preflight.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/vast/pricing.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/providers/weight_cache_preload.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/py.typed +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/runner/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/runner/verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/schema/fields.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/serve/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/serve/deploy.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/serve/export.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/serve/preflight.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/serve/pricing.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/serve/urls.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/__main__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/_deps.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/_locks.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/_runtime.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/app.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/auth.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/billing.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/environment_registry.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/envs.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/projects.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/reconcile.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/routes/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/routes/envs.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/routes/meta.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/routes/serving.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/routes/teacher.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/run_registry.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/server/teacher_broker.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/flash/thinking.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/scripts/build_dev_dist.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/scripts/scrub_history.sh +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/_helpers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/_helpers/runner.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/_helpers/specs.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/_helpers/vast.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/fixtures/math_eval.jsonl +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/fixtures/math_train.jsonl +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/live/__init__.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/live/conftest.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/live/test_lambda_live.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/live/test_runpod_live.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/live/test_vast_live.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_accounting_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_agent_flash_cli_contract.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_algorithms.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_allocator.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_async_uploads.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_backend_jobspec_contract.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_base_model_provenance.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_catalog_consistency.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_commands_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_errors.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_gpus_flag.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_main_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_managed.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_render_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_render_theme.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_traces.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cli_wandb_warning.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_client_server_integration.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_client_stream_reads.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_config_env_vars.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_config_overrides.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cost_analytical.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cost_equation.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cost_hardware.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cost_models.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_cost_rewards.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_deploy_latency.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_dev_channel.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_endpoint_name.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_cache_evict.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_delete.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_download.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_download_api.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_no_install.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_publish.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_pull.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_pull_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_pull_loader_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_pull_managed_control_plane.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_push.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_rate_limit_resolve.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_ref_pinning.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_env_test.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_export.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_flash_mvp.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_flashinfer_cache_dirs.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_github_urlopen_retry.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_gpu_type_single_field.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_gpus.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_grpo_params.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_grpo_resident_sizing.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_health_liveness.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_idle_endpoint_reaper.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_infisical_overlay.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_instance_bootstrap_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_jobs.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_kv_util.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_lambda_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_lambda_runner.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_logging.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_login_perms.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_lora_rank_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_lora_rank_preflight.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_managed_config_fields.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_metrics_schema_agent_contract.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_mig_guard.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_multimodal_input_grads.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_named_reward_metrics.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_oom_escalate_gpu.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_opd.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_opd_limits.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_opd_resume_safety.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_opd_train.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_open_model_policy.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_orchestrator_flash.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_parasail_teacher.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_poll_helpers.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_preflight.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_projects.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_provider_preflight_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_provider_teardown_robustness.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_providers_symmetry.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_realized_cost.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_resolve_params_b.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_rollout_samples.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_runpod_api_delete.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_runpod_key_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_runpod_key_waterfall.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_runpod_slots.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_serve.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_serve_reasoning_content.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_app_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_auth_singleflight.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_checkpoints_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_locks_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_server_standalone.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_serving_context_preflight.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_serving_contract.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_serving_pricing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_serving_schema_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_sft_chunked_nll.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_sft_example_selection.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_sft_gc_off.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_step_metrics.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_teacher_broker.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_thinking_config.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_train_context_preflight.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_vast_api.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_vast_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_vast_offers.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_vast_provider_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_vast_runner.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_verifiers.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_version.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_vl_warmstart_adapter_keys.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_vram_architecture.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_wandb_link.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_wandb_log_coverage.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_wandb_naming.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_warmstart_adapter_download.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_warmstart_cross_repo.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_warmstart_error_attribution.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_worker_dryrun.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_worker_hardexit.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_worker_image.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_worker_init_heartbeat.py +0 -0
- {freesolo_flash_dev-1.0.87 → freesolo_flash_dev-1.0.89}/tests/test_worker_thinking.py +0 -0
|
@@ -268,6 +268,15 @@ 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
|
+
#
|
|
272
|
+
# cachetools/uvicorn/fastapi/torchvision/tqdm/pyarrow are named for the same reason
|
|
273
|
+
# resolve_verl_python names them: verl imports them at MODULE level on the launch path (main_ppo ->
|
|
274
|
+
# ppo.ray_trainer -> rollout.llm_server imports cachetools; rollout.utils imports uvicorn + fastapi)
|
|
275
|
+
# yet declares none of them. Some arrive as transitive vllm deps today, but that is vllm's dependency
|
|
276
|
+
# choice, not a contract verl states. This image sets FLASH_VERL_PYTHON, so live workers never take
|
|
277
|
+
# the resolve_verl_python path where that hardening lives -- letting the two lists drift means the
|
|
278
|
+
# fallback protects nobody and a vllm respin breaks launch on the default image alone. Keep them in
|
|
279
|
+
# lockstep with backend_common.py's install list.
|
|
271
280
|
ARG VERL_SPEC=verl[vllm]@git+https://github.com/freesolo-co/verl@1bea7d6825bbb9d2164e86e379b3680e7c53bb8a
|
|
272
281
|
ENV FLASH_VERL_PYTHON=/opt/verl-venv/bin/python
|
|
273
282
|
RUN printf '%s\n' "numpy==2.2.6" "xgrammar==0.1.25" "vllm==0.19.1" > /tmp/verl-overrides.txt \
|
|
@@ -282,20 +291,41 @@ RUN printf '%s\n' "numpy==2.2.6" "xgrammar==0.1.25" "vllm==0.19.1" > /tmp/verl-o
|
|
|
282
291
|
"TransferQueue==0.1.7" \
|
|
283
292
|
liger-kernel "bitsandbytes>=0.49" qwen-vl-utils \
|
|
284
293
|
"wandb>=0.17" \
|
|
294
|
+
cachetools uvicorn fastapi torchvision tqdm pyarrow \
|
|
295
|
+
"flash-linear-attention @ git+https://github.com/fla-org/flash-linear-attention.git@f0e213dbd8b5fb90c3c7eca869ac1706d5377139" \
|
|
296
|
+
"tilelang==0.1.11" \
|
|
297
|
+
"apache-tvm-ffi==0.1.11" \
|
|
285
298
|
&& rm -f /tmp/verl-overrides.txt \
|
|
286
299
|
&& uv cache clean
|
|
287
300
|
|
|
288
301
|
# flash-attn is REQUIRED in here, not best-effort like it is in the main interpreter above. All three
|
|
289
|
-
# backends
|
|
290
|
-
# CUDA remove-padding path imports
|
|
291
|
-
# (verl/utils/attention_utils.py:30, torch_functional.py:627,657).
|
|
292
|
-
# path: without the module the run dies at the first training
|
|
293
|
-
# below would not catch. Reuses FLASH_ATTN_SPEC so the venv gets
|
|
294
|
-
# interpreter instead of a second source build.
|
|
302
|
+
# backends default remove-padding ON (rl_train.py, sft_train.py, opd_train.py; only a GDN model whose
|
|
303
|
+
# child cannot reset packed boundaries turns it off), and verl's CUDA remove-padding path imports
|
|
304
|
+
# flash_attn.bert_padding UNGUARDED (verl/utils/attention_utils.py:30, torch_functional.py:627,657).
|
|
305
|
+
# There is no SDPA fallback on that path: without the module the run dies at the first training
|
|
306
|
+
# batch, which the import-only assert below would not catch. Reuses FLASH_ATTN_SPEC so the venv gets
|
|
307
|
+
# the same wheel as the main interpreter instead of a second source build.
|
|
295
308
|
RUN TORCH_CUDA_ARCH_LIST="8.0 8.6 8.9 9.0 10.0 12.0" MAX_JOBS=4 \
|
|
296
309
|
uv pip install --python /opt/verl-venv/bin/python --no-build-isolation "${FLASH_ATTN_SPEC}" \
|
|
297
310
|
&& uv cache clean
|
|
298
311
|
|
|
312
|
+
# causal_conv1d in the VERL VENV, which is where the model actually trains. The main interpreter's
|
|
313
|
+
# copy above does not reach here: /opt/verl-venv is built without --system-site-packages, so the two
|
|
314
|
+
# have disjoint paths. Together with fla's cu_seqlens (installed above) this is what makes the packed
|
|
315
|
+
# GDN boundary resets real rather than decorative: engine.worker.backend_common's boundary shim hands
|
|
316
|
+
# the GDN layer cu_seq_lens_q + seq_idx, but transformers' pure-torch fallbacks take **kwargs and
|
|
317
|
+
# silently DISCARD both, so a child missing these kernels trains across example boundaries while
|
|
318
|
+
# looking patched. verl_child_can_reset_gdn_boundaries probes this venv for exactly that reason.
|
|
319
|
+
# Best-effort with the same uninstall-on-broken-import guard as the main interpreter: a present but
|
|
320
|
+
# ABI-broken package passes transformers' find_spec probe and then crashes at model load, and a
|
|
321
|
+
# failed build must degrade to unpacked training (the gate returns False) rather than fail the image.
|
|
322
|
+
RUN TORCH_CUDA_ARCH_LIST="8.0 8.6 8.9 9.0 10.0 12.0" CAUSAL_CONV1D_FORCE_BUILD=TRUE MAX_JOBS=4 \
|
|
323
|
+
uv pip install --python /opt/verl-venv/bin/python "causal-conv1d==1.6.2.post1" --no-build-isolation \
|
|
324
|
+
&& /opt/verl-venv/bin/python -c "import causal_conv1d" \
|
|
325
|
+
&& echo "causal_conv1d (verl venv): installed (GDN boundary resets enabled)" \
|
|
326
|
+
|| { echo "causal_conv1d (verl venv): build/import failed -> GDN trains unpacked (padded fallback)"; \
|
|
327
|
+
uv pip uninstall --python /opt/verl-venv/bin/python causal-conv1d 2>/dev/null || true; }
|
|
328
|
+
|
|
299
329
|
# Sanity: the verl venv must be able to LAUNCH, not merely install. This is the assert that would
|
|
300
330
|
# have caught the shipped-but-unrunnable venv. Import the three entrypoints flash actually invokes
|
|
301
331
|
# (rl_train -> main_ppo, opd_plugin -> main_ppo_sync, sft_train -> sft_trainer; note it is
|
|
@@ -303,6 +333,11 @@ RUN TORCH_CUDA_ARCH_LIST="8.0 8.6 8.9 9.0 10.0 12.0" MAX_JOBS=4 \
|
|
|
303
333
|
# where a version mismatch would surface, and the fork-only rollout field the GRPO path emits as a
|
|
304
334
|
# hydra override. Also assert the Qwen3.5 archs are registered, since that is the entire reason this
|
|
305
335
|
# venv carries 0.19.1 instead of 0.11.2.
|
|
336
|
+
#
|
|
337
|
+
# fla is asserted HARD here (causal_conv1d is not, matching its best-effort install above): without
|
|
338
|
+
# it transformers binds chunk_gated_delta_rule to the pure-torch fallback, which accepts cu_seqlens
|
|
339
|
+
# and discards it, so packed GDN runs would train across example boundaries. A silently fla-less
|
|
340
|
+
# child is exactly the failure this assert exists to make loud at build time rather than on a paid GPU.
|
|
306
341
|
RUN /opt/verl-venv/bin/python -c "import importlib.metadata as M; \
|
|
307
342
|
import importlib; \
|
|
308
343
|
[importlib.import_module(m) for m in ('verl.trainer.main_ppo','verl.trainer.main_ppo_sync','verl.trainer.sft_trainer')]; \
|
|
@@ -310,6 +345,8 @@ import importlib; \
|
|
|
310
345
|
from verl.workers.config.rollout import RolloutConfig; \
|
|
311
346
|
assert 'mask_truncated_completions' in RolloutConfig.__dataclass_fields__, 'verl is not the freesolo fork (no mask_truncated_completions)'; \
|
|
312
347
|
importlib.import_module('flash_attn.bert_padding'); \
|
|
348
|
+
from transformers.utils.import_utils import is_flash_linear_attention_available; \
|
|
349
|
+
assert is_flash_linear_attention_available(), 'flash-linear-attention missing from the verl venv: packed GDN boundary resets would be silently discarded'; \
|
|
313
350
|
assert M.version('verl') == '0.8.0', 'opd structured gate requires verl exactly 0.8.0, got ' + M.version('verl'); \
|
|
314
351
|
assert M.version('xgrammar') == '0.1.25', 'opd structured gate requires xgrammar exactly 0.1.25, got ' + M.version('xgrammar'); \
|
|
315
352
|
from vllm import ModelRegistry; \
|
|
@@ -318,7 +355,9 @@ missing = {'Qwen3_5ForConditionalGeneration','Qwen3_5MoeForConditionalGeneration
|
|
|
318
355
|
assert not missing, 'vllm cannot serve the default-model archs: ' + repr(sorted(missing)); \
|
|
319
356
|
print('verl venv OK | verl', M.version('verl'), '| vllm', M.version('vllm'), '| torch', M.version('torch'), '| numpy', M.version('numpy'), '| xgrammar', M.version('xgrammar')); \
|
|
320
357
|
print(' entrypoints: main_ppo (grpo), main_ppo_sync (opd), sft_trainer (sft)'); \
|
|
321
|
-
print(' qwen3.5 archs registered; mask_truncated_completions present')
|
|
358
|
+
print(' qwen3.5 archs registered; mask_truncated_completions present'); \
|
|
359
|
+
from transformers.utils.import_utils import is_causal_conv1d_available; \
|
|
360
|
+
print(' gdn boundary resets: fla yes, causal_conv1d ' + ('yes' if is_causal_conv1d_available() else 'NO -> gdn trains padded'))"
|
|
322
361
|
|
|
323
362
|
# RunPod Serverless worker entrypoint (Pattern D — Flash custom image). Generate /rp_handler.py
|
|
324
363
|
# from the real _train_body source in providers/runpod/train/endpoints.py (single source of truth;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: freesolo-flash-dev
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.89
|
|
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
|
|
@@ -154,6 +154,48 @@ flash models deploy RUN_ID # serve the trained adapter
|
|
|
154
154
|
flash models chat RUN_ID -m "hello" # talk to it
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
+
### Workload profiles (SFT)
|
|
158
|
+
|
|
159
|
+
The first `flash train` or `flash train --cost` on a new SFT config does not print a quote.
|
|
160
|
+
It reports that no workload profile exists yet and starts one:
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
no exact workload profile exists for this config yet, so there is no training quote to
|
|
164
|
+
print. the server started a separate profile run that loads your environment and tokenizes
|
|
165
|
+
the exact dataset this training would consume.
|
|
166
|
+
that profile run is real work and is billed on its own (estimated $0.03); no training run
|
|
167
|
+
was created, no training gpu was allocated, and nothing was charged for training.
|
|
168
|
+
follow it with `flash runs status profile-sft-...`, then re-run this command once it
|
|
169
|
+
reports done.
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
A profile run loads your environment at its pinned SHA, renders and tokenizes every example
|
|
173
|
+
this config would train on, and records aggregates: retained and dropped examples, tokens per
|
|
174
|
+
epoch, supervised tokens, realized max length, packed blocks, and the update horizon. The SFT
|
|
175
|
+
quote is then computed from those measured tokens rather than from an assumed average example
|
|
176
|
+
length, and the same profile is what the training worker trains from.
|
|
177
|
+
|
|
178
|
+
Consequences worth knowing before you submit:
|
|
179
|
+
|
|
180
|
+
- **Profiles are separate runs and separate charges.** A profile appears in `flash runs list`
|
|
181
|
+
under its own id and is billed for its own (CPU-only, short) work. It is never rolled into
|
|
182
|
+
the training charge, and a failed profile cannot become training spend.
|
|
183
|
+
- **Quoting fails closed.** If no trustworthy matching profile exists, no training run is
|
|
184
|
+
created, no GPU is allocated, and no quote is persisted. There is no fallback estimate.
|
|
185
|
+
- **The cache key is the workload, not the run.** Profile ids are derived from environment id,
|
|
186
|
+
resolved SHA and params; model, revision and tokenizer revision; `seed`; `thinking`; worker
|
|
187
|
+
env; and the `[train]` fields `epochs`, `batch_size`, `max_context_tokens`, `max_steps` and
|
|
188
|
+
`max_examples`. Change any of them and it is a different workload needing its own profile.
|
|
189
|
+
Everything else about a run, including which GPU it lands on, is outside the key.
|
|
190
|
+
- **Profiles are shared across users.** The id is a hash of that workload, not of your account,
|
|
191
|
+
so if someone else already measured your exact config you wait for their profile and are not
|
|
192
|
+
charged for a second one. Their run is not readable by your key, so the CLI tells you to
|
|
193
|
+
wait rather than pointing you at a run id that would answer 404.
|
|
194
|
+
- **A failed profile is retried, not final.** If a profile fails or is cancelled, the next
|
|
195
|
+
submission of that config starts a replacement rather than reporting the workload as
|
|
196
|
+
permanently unquotable. Because the id is shared, exactly one of the waiting submitters
|
|
197
|
+
launches the replacement and the rest wait on it.
|
|
198
|
+
|
|
157
199
|
Run management lives under `flash runs` (`status`, `log`, `cancel`, `checkpoint`) and
|
|
158
200
|
serving under `flash models` (`deploy`, `chat`, `deployments`, `undeploy`, `export`).
|
|
159
201
|
`flash models` on its own lists supported base models and `flash gpus` lists GPU classes
|
|
@@ -91,6 +91,48 @@ flash models deploy RUN_ID # serve the trained adapter
|
|
|
91
91
|
flash models chat RUN_ID -m "hello" # talk to it
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
+
### Workload profiles (SFT)
|
|
95
|
+
|
|
96
|
+
The first `flash train` or `flash train --cost` on a new SFT config does not print a quote.
|
|
97
|
+
It reports that no workload profile exists yet and starts one:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
no exact workload profile exists for this config yet, so there is no training quote to
|
|
101
|
+
print. the server started a separate profile run that loads your environment and tokenizes
|
|
102
|
+
the exact dataset this training would consume.
|
|
103
|
+
that profile run is real work and is billed on its own (estimated $0.03); no training run
|
|
104
|
+
was created, no training gpu was allocated, and nothing was charged for training.
|
|
105
|
+
follow it with `flash runs status profile-sft-...`, then re-run this command once it
|
|
106
|
+
reports done.
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
A profile run loads your environment at its pinned SHA, renders and tokenizes every example
|
|
110
|
+
this config would train on, and records aggregates: retained and dropped examples, tokens per
|
|
111
|
+
epoch, supervised tokens, realized max length, packed blocks, and the update horizon. The SFT
|
|
112
|
+
quote is then computed from those measured tokens rather than from an assumed average example
|
|
113
|
+
length, and the same profile is what the training worker trains from.
|
|
114
|
+
|
|
115
|
+
Consequences worth knowing before you submit:
|
|
116
|
+
|
|
117
|
+
- **Profiles are separate runs and separate charges.** A profile appears in `flash runs list`
|
|
118
|
+
under its own id and is billed for its own (CPU-only, short) work. It is never rolled into
|
|
119
|
+
the training charge, and a failed profile cannot become training spend.
|
|
120
|
+
- **Quoting fails closed.** If no trustworthy matching profile exists, no training run is
|
|
121
|
+
created, no GPU is allocated, and no quote is persisted. There is no fallback estimate.
|
|
122
|
+
- **The cache key is the workload, not the run.** Profile ids are derived from environment id,
|
|
123
|
+
resolved SHA and params; model, revision and tokenizer revision; `seed`; `thinking`; worker
|
|
124
|
+
env; and the `[train]` fields `epochs`, `batch_size`, `max_context_tokens`, `max_steps` and
|
|
125
|
+
`max_examples`. Change any of them and it is a different workload needing its own profile.
|
|
126
|
+
Everything else about a run, including which GPU it lands on, is outside the key.
|
|
127
|
+
- **Profiles are shared across users.** The id is a hash of that workload, not of your account,
|
|
128
|
+
so if someone else already measured your exact config you wait for their profile and are not
|
|
129
|
+
charged for a second one. Their run is not readable by your key, so the CLI tells you to
|
|
130
|
+
wait rather than pointing you at a run id that would answer 404.
|
|
131
|
+
- **A failed profile is retried, not final.** If a profile fails or is cancelled, the next
|
|
132
|
+
submission of that config starts a replacement rather than reporting the workload as
|
|
133
|
+
permanently unquotable. Because the id is shared, exactly one of the waiting submitters
|
|
134
|
+
launches the replacement and the rest wait on it.
|
|
135
|
+
|
|
94
136
|
Run management lives under `flash runs` (`status`, `log`, `cancel`, `checkpoint`) and
|
|
95
137
|
serving under `flash models` (`deploy`, `chat`, `deployments`, `undeploy`, `export`).
|
|
96
138
|
`flash models` on its own lists supported base models and `flash gpus` lists GPU classes
|
|
@@ -370,7 +370,8 @@ MODELS: dict[str, ModelInfo] = {
|
|
|
370
370
|
algos=ALGORITHMS,
|
|
371
371
|
min_vram_gb=48,
|
|
372
372
|
min_disk_gb=120,
|
|
373
|
-
# NOT QLoRA: peft bnb merge during GRPO rollout diverges
|
|
373
|
+
# NOT QLoRA: a peft bnb merge during GRPO rollout diverges the rollout's precision from the
|
|
374
|
+
# trainer's, and the importance ratio those two log-probs form collapses to 0.
|
|
374
375
|
grpo_min_vram_gb=80,
|
|
375
376
|
quant="bf16",
|
|
376
377
|
recommended_gpu="A100 PCIe",
|
|
@@ -1024,8 +1024,7 @@ watch `truncation_rate`, which counts completions not ending in EOS and is not s
|
|
|
1024
1024
|
For pure multi-turn GRPO, Flash gives each Flash-owned vLLM generation request a managed
|
|
1025
1025
|
10-to-60-minute absolute deadline and at most two physical attempts. A timed-out request is
|
|
1026
1026
|
aborted before retry. Enforcement is cooperative between engine polls, and there is no total
|
|
1027
|
-
episode elapsed-time cutoff. This policy does not time out OPD
|
|
1028
|
-
environment calls.
|
|
1027
|
+
episode elapsed-time cutoff. This policy does not time out OPD or environment calls.
|
|
1029
1028
|
|
|
1030
1029
|
> **The reward-hacking signature:** a smoothed reward rising while mean generated
|
|
1031
1030
|
> length collapses. Whenever any shortness or format pressure is active, verify the
|
|
@@ -1039,13 +1038,11 @@ environment calls.
|
|
|
1039
1038
|
|
|
1040
1039
|
**First, check that per-turn credit is available to you at all.** It is supported on the
|
|
1041
1040
|
pure multi-turn rollout path — an `EnvironmentMultiTurn` that drives its own turn loop. A
|
|
1042
|
-
multi-turn environment that exposes **tools**
|
|
1043
|
-
|
|
1044
|
-
`
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
your environment exposes tools, stay on `per_episode`; emitting `per_turn_rewards` does not
|
|
1048
|
-
unlock it.
|
|
1041
|
+
multi-turn environment that exposes **tools** is not supported at all: the worker raises
|
|
1042
|
+
`verl grpo does not support openai function-calling tool environments; this env reports
|
|
1043
|
+
is_tool_env` and the run dies at startup. Nothing catches this earlier —
|
|
1044
|
+
`flash train --dry-run` accepts the config, because the check depends on the environment
|
|
1045
|
+
object, not the spec.
|
|
1049
1046
|
|
|
1050
1047
|
On the pure multi-turn path, setting `credit_assignment = "per_turn"` is only _half_ the
|
|
1051
1048
|
request. The trainer reads the
|
|
@@ -89,7 +89,7 @@ _HELP_GROUPS: list[tuple[str, list[tuple[str, str]]]] = [
|
|
|
89
89
|
("env setup", "scaffold a starter Freesolo environment"),
|
|
90
90
|
("env list", "list local environment sources"),
|
|
91
91
|
("env test", "validate a local environment offline"),
|
|
92
|
-
("env eval", "score
|
|
92
|
+
("env eval", "score a deployed model against its published environment's suites"),
|
|
93
93
|
("env push", "upload a local environment"),
|
|
94
94
|
("env pull", "download a published environment or file"),
|
|
95
95
|
("env delete", "delete a published environment"),
|
|
@@ -396,20 +396,17 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
396
396
|
|
|
397
397
|
env_eval = env_sub.add_parser(
|
|
398
398
|
"eval",
|
|
399
|
-
help="score
|
|
400
|
-
description=
|
|
399
|
+
help="score a deployed model against its own environment's held-out suites",
|
|
400
|
+
description=(
|
|
401
|
+
"score a deployed model against the held-out suites of the published environment "
|
|
402
|
+
"its run trains on"
|
|
403
|
+
),
|
|
401
404
|
)
|
|
402
405
|
env_eval.add_argument(
|
|
403
406
|
"target",
|
|
404
407
|
metavar="TARGET",
|
|
405
408
|
help="a bare RUN_ID, RUN_ID/step-N, or full immutable adapter revision",
|
|
406
409
|
)
|
|
407
|
-
env_eval.add_argument(
|
|
408
|
-
"path",
|
|
409
|
-
nargs="?",
|
|
410
|
-
default=".",
|
|
411
|
-
help="local environment directory or environment.py path",
|
|
412
|
-
)
|
|
413
410
|
# the same two knobs `env test` exposes, and for the same reason: an env whose
|
|
414
411
|
# `load_environment()` requires a difficulty or reads a non-default split cannot be evaluated
|
|
415
412
|
# at all without them, and a held-out suite scored against a differently-configured
|
|
@@ -449,14 +446,17 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
449
446
|
help=f"parallel model requests (1-{_MAX_CONCURRENCY})",
|
|
450
447
|
)
|
|
451
448
|
env_eval.add_argument(
|
|
452
|
-
"--upload",
|
|
453
|
-
|
|
454
|
-
|
|
449
|
+
"--no-upload",
|
|
450
|
+
dest="upload",
|
|
451
|
+
action="store_false",
|
|
452
|
+
help="score locally without recording the results in the dashboard",
|
|
455
453
|
)
|
|
456
454
|
env_eval.add_argument(
|
|
457
455
|
"--project",
|
|
458
456
|
metavar="PROJECT_ID",
|
|
459
|
-
help=
|
|
457
|
+
help=(
|
|
458
|
+
"project id to record results under (default: the project the evaluated run belongs to)"
|
|
459
|
+
),
|
|
460
460
|
)
|
|
461
461
|
env_eval.set_defaults(func=cmd_env_eval)
|
|
462
462
|
|
|
@@ -749,8 +749,10 @@ def _warn_if_login_shadowed(args) -> None:
|
|
|
749
749
|
"""Surface an ambient FREESOLO_API_KEY that binds a command to another org."""
|
|
750
750
|
if getattr(args, "func", None) not in _ORG_BINDING_COMMANDS:
|
|
751
751
|
return
|
|
752
|
-
# `train --cost`
|
|
753
|
-
#
|
|
752
|
+
# `train --cost` cannot be classified here: the algorithm is only known once the config is
|
|
753
|
+
# parsed, which happens inside the command. grpo/opd stay catalog-only and must not warn about
|
|
754
|
+
# an org they never reach; sft authenticates and can start a billed profile run, so it emits
|
|
755
|
+
# this warning itself (see commands._cmd_train_cost_sft).
|
|
754
756
|
if getattr(args, "cost", False):
|
|
755
757
|
return
|
|
756
758
|
message = shadowed_login_warning()
|
|
@@ -21,7 +21,11 @@ from flash.client import (
|
|
|
21
21
|
save_credentials,
|
|
22
22
|
verify_freesolo_key,
|
|
23
23
|
)
|
|
24
|
-
from flash.client.config import
|
|
24
|
+
from flash.client.config import (
|
|
25
|
+
load_credentials,
|
|
26
|
+
load_credentials_with_source,
|
|
27
|
+
shadowed_login_warning,
|
|
28
|
+
)
|
|
25
29
|
from flash.client.runtime_secrets import runtime_secrets_from_local_env
|
|
26
30
|
from flash.client.specs import spec_payload
|
|
27
31
|
from flash.cost.spec import runconfig_from_spec
|
|
@@ -29,7 +33,6 @@ from flash.runner import TERMINAL_STATES
|
|
|
29
33
|
from flash.schema import (
|
|
30
34
|
ConfigError,
|
|
31
35
|
spec_and_train_keys_from_file,
|
|
32
|
-
spec_from_file,
|
|
33
36
|
train_schema_metadata,
|
|
34
37
|
)
|
|
35
38
|
from flash.serve.urls import is_freesolo_hosted_url
|
|
@@ -320,12 +323,14 @@ def cmd_env_list(args) -> int:
|
|
|
320
323
|
def _cmd_train_cost(args) -> int:
|
|
321
324
|
"""`flash train --cost`: print the pre-flight USD cost for the config and exit (no submit).
|
|
322
325
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
+
grpo and opd quote offline from the catalog. sft has no offline quote at all: its cost is
|
|
327
|
+
derived from the exact tokenized dataset, and only a workload profile knows that, so sft asks
|
|
328
|
+
the server for the same profile-backed quote a real submit would freeze. There is deliberately
|
|
329
|
+
no analytical sft fallback -- a guessed row count is what this whole path exists to remove.
|
|
330
|
+
"""
|
|
326
331
|
from flash.lora_rank import preflight_train_context_within_serving
|
|
327
332
|
|
|
328
|
-
spec =
|
|
333
|
+
spec, authored_train_keys = spec_and_train_keys_from_file(
|
|
329
334
|
args.config,
|
|
330
335
|
run_id=None,
|
|
331
336
|
overrides=args.overrides,
|
|
@@ -333,6 +338,15 @@ def _cmd_train_cost(args) -> int:
|
|
|
333
338
|
project_required=True,
|
|
334
339
|
)
|
|
335
340
|
preflight_train_context_within_serving(spec)
|
|
341
|
+
if spec.algorithm == "sft":
|
|
342
|
+
return _cmd_train_cost_sft(args, spec, authored_train_keys)
|
|
343
|
+
return _cmd_train_cost_offline(spec)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _cmd_train_cost_offline(spec) -> int:
|
|
347
|
+
"""Catalog-only quote for the algorithms that do not need workload evidence yet (grpo, opd)."""
|
|
348
|
+
from flash.cost import estimate_cost
|
|
349
|
+
|
|
336
350
|
if spec.train.init_from_adapter:
|
|
337
351
|
# --cost is offline/catalog-only and cannot read the source adapter, so the rank stays at the
|
|
338
352
|
# local default. Warm starts train and are priced at the SOURCE adapter's authoritative rank
|
|
@@ -352,6 +366,168 @@ def _cmd_train_cost(args) -> int:
|
|
|
352
366
|
return 0
|
|
353
367
|
|
|
354
368
|
|
|
369
|
+
def _cmd_train_cost_sft(args, spec, authored_train_keys: frozenset[str]) -> int:
|
|
370
|
+
"""Exact sft quote, served by the same authenticated path that freezes a real submit's quote."""
|
|
371
|
+
# cli._warn_if_login_shadowed() suppresses this warning for `--cost` because the catalog path
|
|
372
|
+
# never reaches an organization. the sft path does: it authenticates, resolves the project, and
|
|
373
|
+
# can start a billed profile run, so the warning has to fire here after all.
|
|
374
|
+
message = shadowed_login_warning()
|
|
375
|
+
if message:
|
|
376
|
+
print(render.warn(message) if render.styled() else f"warning: {message}", file=sys.stderr)
|
|
377
|
+
client = client_from_config()
|
|
378
|
+
try:
|
|
379
|
+
status = client.create_run(
|
|
380
|
+
spec_payload(spec, authored_train_keys=authored_train_keys),
|
|
381
|
+
runtime_secrets=runtime_secrets_from_local_env(
|
|
382
|
+
args.config, keys=spec.environment.secrets
|
|
383
|
+
)
|
|
384
|
+
or None,
|
|
385
|
+
dry_run=True,
|
|
386
|
+
client_train_schema=_client_train_schema(authored_train_keys),
|
|
387
|
+
)
|
|
388
|
+
except ApiError as exc:
|
|
389
|
+
_raise_if_workload_profile_pending(client, exc)
|
|
390
|
+
detail = _legacy_train_key_rejection_detail(exc, authored_train_keys)
|
|
391
|
+
if detail is None:
|
|
392
|
+
raise
|
|
393
|
+
raise ApiError(exc.status, detail, detail=detail) from exc
|
|
394
|
+
_print_exact_sft_cost(status, spec)
|
|
395
|
+
return 0
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def _client_train_schema(authored_train_keys: frozenset[str]) -> dict:
|
|
399
|
+
return {
|
|
400
|
+
"version": __version__,
|
|
401
|
+
"fields": train_schema_metadata(),
|
|
402
|
+
"authored_keys": sorted(authored_train_keys),
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
def _raise_if_workload_profile_pending(client: ApiClient, exc: ApiError) -> None:
|
|
407
|
+
"""Explain a profile-pending rejection and fail, or return so the caller keeps handling `exc`.
|
|
408
|
+
|
|
409
|
+
A miss is not a validation error: the server started a real, separately billed profile run that
|
|
410
|
+
tokenizes the exact dataset. Saying only "409" would leave the user with a charge they cannot
|
|
411
|
+
see the reason for, and re-running blindly would look like the same request failing twice.
|
|
412
|
+
"""
|
|
413
|
+
detail = exc.detail
|
|
414
|
+
if exc.code != "workload_profile_pending" or not isinstance(detail, dict):
|
|
415
|
+
return
|
|
416
|
+
profile_run_id = str(detail.get("profile_run_id") or "")
|
|
417
|
+
state = str(detail.get("state") or "unknown")
|
|
418
|
+
# the profile id is deterministic in the workload, so this config's profile may already be
|
|
419
|
+
# running under another key. that run is not readable here and is not billed here either, so
|
|
420
|
+
# both the follow-up command and the charge sentence have to change.
|
|
421
|
+
owned = detail.get("owned") is not False
|
|
422
|
+
charge = _profile_charge(client, profile_run_id) if owned else None
|
|
423
|
+
if owned:
|
|
424
|
+
lines = [
|
|
425
|
+
"no exact workload profile exists for this config yet, so there is no training quote "
|
|
426
|
+
"to print. the server started a separate profile run that loads your environment and "
|
|
427
|
+
"tokenizes the exact dataset this training would consume.",
|
|
428
|
+
"that profile run is real work and is billed on its own"
|
|
429
|
+
+ (f" (estimated ${charge:.2f})" if charge is not None else "")
|
|
430
|
+
+ "; no training run was created, no training gpu was allocated, and nothing was "
|
|
431
|
+
"charged for training.",
|
|
432
|
+
f"follow it with `{CLI_NAME} runs status {profile_run_id}`, then re-run this command "
|
|
433
|
+
"once it reports done."
|
|
434
|
+
if profile_run_id
|
|
435
|
+
else "re-run this command once the profile reports done.",
|
|
436
|
+
]
|
|
437
|
+
else:
|
|
438
|
+
lines = [
|
|
439
|
+
"no exact workload profile exists for this config yet, so there is no training quote "
|
|
440
|
+
"to print. one is already being measured for this exact config and will be reused, so "
|
|
441
|
+
"nothing was started or charged here.",
|
|
442
|
+
"re-run this command in a few minutes.",
|
|
443
|
+
]
|
|
444
|
+
for line in lines:
|
|
445
|
+
print(render.note(line) if render.styled() else line, file=sys.stderr)
|
|
446
|
+
raise ClientError(
|
|
447
|
+
f"workload profile {profile_run_id or '(unknown)'} is {state}; "
|
|
448
|
+
"the exact quote is available once it succeeds"
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def _profile_charge(client: ApiClient, profile_run_id: str) -> float | None:
|
|
453
|
+
"""The profile run's own quote, or None when it cannot be read (not owned by this key, etc)."""
|
|
454
|
+
if not profile_run_id:
|
|
455
|
+
return None
|
|
456
|
+
try:
|
|
457
|
+
status = client.get_run(profile_run_id)
|
|
458
|
+
except (ApiError, ClientError):
|
|
459
|
+
return None
|
|
460
|
+
quote = status.get("estimated_cost_usd") if isinstance(status, dict) else None
|
|
461
|
+
# bool is an int subclass, so an unchecked isinstance would render a JSON `true` as "$1.00" --
|
|
462
|
+
# a charge the user is told to expect that no profile ever quoted.
|
|
463
|
+
if not isinstance(quote, (int, float)) or isinstance(quote, bool):
|
|
464
|
+
return None
|
|
465
|
+
return float(quote)
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def _exact_sft_cost_rows(spec, profile: dict) -> list[tuple[str, str | None]]:
|
|
469
|
+
"""Rows describing the exact measured workload behind an sft quote.
|
|
470
|
+
|
|
471
|
+
Only aggregates the server actually returned are shown. A field that is absent is dropped
|
|
472
|
+
rather than defaulted, so the panel never reports a count the profile did not measure.
|
|
473
|
+
"""
|
|
474
|
+
|
|
475
|
+
def count(key: str) -> int | None:
|
|
476
|
+
value = profile.get(key)
|
|
477
|
+
return value if isinstance(value, int) and not isinstance(value, bool) else None
|
|
478
|
+
|
|
479
|
+
steps = count("authoritative_steps")
|
|
480
|
+
retained, selected = count("retained_examples"), count("selected_examples")
|
|
481
|
+
dropped = count("dropped_examples")
|
|
482
|
+
compute, supervised = (
|
|
483
|
+
count("authoritative_compute_tokens"),
|
|
484
|
+
count("authoritative_supervised_tokens"),
|
|
485
|
+
)
|
|
486
|
+
packing = str(profile.get("packing_mode") or "")
|
|
487
|
+
architecture = str(profile.get("architecture_mode") or "")
|
|
488
|
+
digest = str(profile.get("content_digest") or "")
|
|
489
|
+
examples = None
|
|
490
|
+
if retained is not None and selected is not None:
|
|
491
|
+
examples = f"{retained:,} trained of {selected:,} selected"
|
|
492
|
+
if dropped:
|
|
493
|
+
examples += f" ({dropped:,} dropped)"
|
|
494
|
+
tokens = None
|
|
495
|
+
if compute is not None:
|
|
496
|
+
tokens = f"{compute:,} compute"
|
|
497
|
+
if supervised is not None:
|
|
498
|
+
tokens += f", {supervised:,} supervised"
|
|
499
|
+
return [
|
|
500
|
+
("run", f"{spec.model} [SFT{f', {steps} steps' if steps is not None else ''}]"),
|
|
501
|
+
("workload", f"{packing} ({architecture})" if packing and architecture else None),
|
|
502
|
+
("examples", examples),
|
|
503
|
+
("tokens", tokens),
|
|
504
|
+
("profile", digest[:12] or None),
|
|
505
|
+
]
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def _print_exact_sft_cost(status: dict, spec) -> None:
|
|
509
|
+
total = status.get("estimated_cost_usd") if isinstance(status, dict) else None
|
|
510
|
+
if not isinstance(total, (int, float)) or isinstance(total, bool):
|
|
511
|
+
raise ClientError(
|
|
512
|
+
"the server accepted this SFT config but returned no cost estimate; "
|
|
513
|
+
f"run `{CLI_NAME} train --dry-run` to see the full server response"
|
|
514
|
+
)
|
|
515
|
+
profile = status.get("workload_profile")
|
|
516
|
+
rows = _exact_sft_cost_rows(spec, profile if isinstance(profile, dict) else {})
|
|
517
|
+
if render.styled():
|
|
518
|
+
print(render.exact_cost_panel(rows, float(total)))
|
|
519
|
+
else:
|
|
520
|
+
for key, value in rows:
|
|
521
|
+
if value is not None:
|
|
522
|
+
print(f"{key.ljust(8)}: {value}")
|
|
523
|
+
print(f"{'TOTAL'.ljust(8)}: ${float(total):.2f}")
|
|
524
|
+
print(
|
|
525
|
+
"quoted from the exact tokenized workload, not an assumed row count. no training gpu was "
|
|
526
|
+
"allocated and nothing was charged for training.",
|
|
527
|
+
file=sys.stderr,
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
|
|
355
531
|
def _legacy_train_key_rejection_detail(
|
|
356
532
|
exc: ApiError, authored_train_keys: frozenset[str]
|
|
357
533
|
) -> str | None:
|
|
@@ -443,18 +619,16 @@ def cmd_train(args) -> int:
|
|
|
443
619
|
)
|
|
444
620
|
payload = spec_payload(spec, authored_train_keys=authored_train_keys)
|
|
445
621
|
client = client_from_config()
|
|
446
|
-
client_train_schema =
|
|
447
|
-
"version": __version__,
|
|
448
|
-
"fields": train_schema_metadata(),
|
|
449
|
-
"authored_keys": sorted(authored_train_keys),
|
|
450
|
-
}
|
|
622
|
+
client_train_schema = _client_train_schema(authored_train_keys)
|
|
451
623
|
runtime_secrets = (
|
|
452
624
|
runtime_secrets_from_local_env(args.config, keys=spec.environment.secrets) or None
|
|
453
625
|
)
|
|
454
626
|
_warn_if_wandb_requested_without_key(spec, runtime_secrets, dry_run=bool(args.dry_run))
|
|
455
627
|
if args.dry_run:
|
|
456
|
-
# dry-run runs submit-time server preflights without
|
|
457
|
-
#
|
|
628
|
+
# dry-run runs submit-time server preflights without allocating a training gpu or charging
|
|
629
|
+
# for training. a rejection surfaces as the server's error with exit status 1. for sft it
|
|
630
|
+
# also requires an exact workload profile, so a miss starts a separate billed profile run
|
|
631
|
+
# (see _raise_if_workload_profile_pending) instead of previewing anything.
|
|
458
632
|
try:
|
|
459
633
|
status = client.create_run(
|
|
460
634
|
payload,
|
|
@@ -463,10 +637,11 @@ def cmd_train(args) -> int:
|
|
|
463
637
|
client_train_schema=client_train_schema,
|
|
464
638
|
)
|
|
465
639
|
except ApiError as exc:
|
|
640
|
+
_raise_if_workload_profile_pending(client, exc)
|
|
466
641
|
detail = _legacy_train_key_rejection_detail(exc, authored_train_keys)
|
|
467
642
|
if detail is None:
|
|
468
643
|
raise
|
|
469
|
-
raise ApiError(exc.status, detail) from exc
|
|
644
|
+
raise ApiError(exc.status, detail, detail=detail) from exc
|
|
470
645
|
compatibility = status.pop("train_schema_compatibility", None)
|
|
471
646
|
_print_train_schema_compatibility(compatibility)
|
|
472
647
|
# the server fails open on a billing-infra problem, so "cost" is only in the validated list
|
|
@@ -474,12 +649,22 @@ def cmd_train(args) -> int:
|
|
|
474
649
|
# equally not a verification -- so treat anything but an explicit True as unverified.
|
|
475
650
|
affordability_verified = status.pop("affordability_verified", None) is True
|
|
476
651
|
cost = "and cost" if affordability_verified else "but NOT cost"
|
|
652
|
+
# sft additionally required a matching workload profile to get this far, and that profile
|
|
653
|
+
# run already imported environment.py and tokenized the dataset. claiming otherwise here
|
|
654
|
+
# would understate what has been checked -- and what has already been billed.
|
|
655
|
+
environment = (
|
|
656
|
+
"your environment.py and the exact dataset were already loaded and tokenized by the "
|
|
657
|
+
"workload profile this quote is built on; model load and gpu/training are first "
|
|
658
|
+
"exercised on the worker after cold-start."
|
|
659
|
+
if spec.algorithm == "sft"
|
|
660
|
+
else "it did NOT import or run your environment.py; dataset loading, "
|
|
661
|
+
"start_episode/episode shapes, reward/scorer, worker imports, model load, and "
|
|
662
|
+
"gpu/training are first exercised on the worker after cold-start."
|
|
663
|
+
)
|
|
477
664
|
print(
|
|
478
665
|
"dry-run validated: config/schema, model+algorithm compatibility, lora rank, "
|
|
479
|
-
f"runtime-secret presence, warm-start source, serving context cap, {cost}.
|
|
480
|
-
"
|
|
481
|
-
"reward/scorer, worker imports, model load, and gpu/training are first exercised on the "
|
|
482
|
-
"worker after cold-start.",
|
|
666
|
+
f"runtime-secret presence, warm-start source, serving context cap, {cost}. "
|
|
667
|
+
f"{environment}",
|
|
483
668
|
file=sys.stderr,
|
|
484
669
|
)
|
|
485
670
|
if not affordability_verified:
|
|
@@ -497,11 +682,18 @@ def cmd_train(args) -> int:
|
|
|
497
682
|
else:
|
|
498
683
|
print(json.dumps(status, indent=2))
|
|
499
684
|
return 0
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
685
|
+
try:
|
|
686
|
+
status = client.create_run(
|
|
687
|
+
payload,
|
|
688
|
+
runtime_secrets=runtime_secrets,
|
|
689
|
+
client_train_schema=client_train_schema,
|
|
690
|
+
)
|
|
691
|
+
except ApiError as exc:
|
|
692
|
+
# a real submit misses the profile cache the same way a preview does, and the miss starts a
|
|
693
|
+
# separately billed profile run. without this the user sees a bare 409 for a charge they
|
|
694
|
+
# were never told about (see _raise_if_workload_profile_pending).
|
|
695
|
+
_raise_if_workload_profile_pending(client, exc)
|
|
696
|
+
raise
|
|
505
697
|
run_id = status["run_id"]
|
|
506
698
|
logger.info(
|
|
507
699
|
"submitted run %s: model=%s algorithm=%s gpu=%s",
|