freesolo-flash-dev 0.2.31__tar.gz → 0.2.32__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-0.2.31 → freesolo_flash_dev-0.2.32}/PKG-INFO +19 -17
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/README.md +18 -16
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/cli/__init__.py +5 -3
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/cli/commands.py +8 -9
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/cli/render.py +23 -14
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/cli/training_doc.py +55 -10
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/cost/types.py +12 -3
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/envs/adapter.py +41 -1
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/base.py +32 -36
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/lambdalabs/pricing.py +0 -6
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/runpod/pricing.py +0 -2
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/runpod/train/__init__.py +12 -1
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/runpod/train/endpoints.py +12 -5
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/serve/deploy.py +20 -12
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/serve/export.py +9 -13
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/routes/serving.py +7 -10
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/pyproject.toml +2 -2
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_cli_commands.py +18 -7
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_cli_render_theme.py +13 -15
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_env_pull.py +27 -3
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_export.py +27 -1
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_flash_worker.py +11 -8
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_gpus.py +9 -39
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_orchestrator_flash.py +77 -9
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_serve.py +0 -19
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_server_api.py +28 -9
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_serving_contract.py +2 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/uv.lock +1 -1
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/.dockerignore +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/.env.example +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/.github/workflows/auto-rebake.yml +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/.github/workflows/bake-kernel-cache.yml +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/.github/workflows/ci.yml +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/.github/workflows/main-source-guard.yml +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/.github/workflows/publish-dev.yml +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/.github/workflows/publish-image.yml +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/.github/workflows/publish.yml +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/.github/workflows/version-parity.yml +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/.github/workflows/worker-image.yml +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/.gitignore +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/Dockerfile +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/Dockerfile.worker +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/LICENSE +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/build/kernel_cache/.gitignore +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/build/kernel_cache/.keep +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docker/Dockerfile.kernelcache +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docker/Dockerfile.kernelcache.relayer +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docker/bake_kernel_cache.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docker/bake_pod_entry.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docker/kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docker/make_rp_handler.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docs/cli-style/README.md +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docs/cli-style/generate.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docs/cli-style/index.html +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docs/cli-style/preview.png +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docs/cli-style/themed-errors.png +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docs/cli-theme/README.md +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docs/cli-theme/before-after-dark.png +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docs/cli-theme/before-after-light.png +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docs/cli-theme/gallery-dark.png +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docs/cli-theme/gallery-light.png +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docs/cli-theme/help-dark.png +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docs/cli-theme/help-light.png +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/docs/kernel-cache.md +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/_channel.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/_fileio.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/_logging.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/_update_check.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/catalog.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/cli/__main__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/cli/envpush.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/client/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/client/config.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/client/http.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/client/runtime_secrets.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/client/specs.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/cost/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/cost/analytical.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/cost/facts.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/cost/spec.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/accounting.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/chalk_kernels.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/multiturn_rollout.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/recipe.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/vram.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/__main__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/_pkg.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/adapter.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/decoding.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/finalize.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/gpu_setup.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/grpo.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/heartbeat.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/hf.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/kernel_warmup.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/lora.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/packing.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/perf/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/perf/attn.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/perf/diagnostics.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/perf/lifecycle.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/perf/liger.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/perf/loraplus.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/perf/memory.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/rl.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/sft.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/engine/worker/wandb_log.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/envs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/envs/base.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/envs/pull.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/envs/registry.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/_auth.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/_http.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/_instance.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/_instance_bootstrap.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/_poll.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/allocator.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/lambdalabs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/lambdalabs/api.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/lambdalabs/auth.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/lambdalabs/gpus.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/lambdalabs/jobs/builders.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/lambdalabs/preflight.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/lambdalabs/train.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/preflight.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/realized.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/runpod/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/runpod/api.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/runpod/auth.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/runpod/cost.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/runpod/gpus.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/runpod/jobs.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/runpod/keys.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/runpod/preflight.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/runpod/preload.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/runpod/slots.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/providers/runpod/train/deps.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/py.typed +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/runner/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/runner/checkpoints.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/runner/deploy.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/runner/lifecycle.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/schema/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/schema/fields.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/serve/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/serve/pricing.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/__main__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/_deps.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/_internal_client.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/_locks.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/_runtime.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/app.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/auth.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/billing.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/billing_retry.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/checkpoints.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/db.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/environment_registry.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/envs.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/reconcile.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/routes/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/routes/envs.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/routes/meta.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/routes/runs.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/server/run_registry.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/flash/spec.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/infisical-entrypoint.sh +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/scripts/build_dev_dist.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/_helpers/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/_helpers/runner.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/_helpers/specs.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/conftest.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/fixtures/math_eval.jsonl +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/fixtures/math_train.jsonl +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/live/__init__.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/live/conftest.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/live/test_lambda_live.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/live/test_runpod_live.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_agent_flash_cli_contract.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_algorithms.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_allocator.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_b200_rollout_opt.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_backend_jobspec_contract.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_billing_retry.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_cancel_remote.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_catalog_consistency.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_chalk_kernels.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_checkpoints.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_cli_errors.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_cli_estimate.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_cli_help.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_cli_managed.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_client.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_client_server_integration.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_config_overrides.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_cost_analytical.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_cost_equation.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_cost_estimate.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_cost_hardware.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_cost_models.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_cost_rewards.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_dev_channel.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_disk_gb.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_endpoint_name.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_env_delete.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_env_no_install.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_env_publish.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_env_push.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_env_rate_limit_resolve.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_envs_coverage.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_flash_mvp.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_flashinfer_cache_dirs.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_github_urlopen_retry.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_grpo_mask_aware.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_grpo_params.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_grpo_sleep_gate.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_idle_endpoint_reaper.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_internal_client.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_jobs.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_kernel_cache.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_kv_util.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_lambda_runner.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_logging.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_login_perms.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_managed_hf_repo.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_metrics_schema_agent_contract.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_mig_guard.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_multiturn_rollout.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_oom_escalate_gpu.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_open_model_policy.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_packing.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_poll_helpers.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_preflight.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_provider_routing.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_provider_teardown_robustness.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_providers_symmetry.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_realized_cost.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_resolve_params_b.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_resume_on_retry.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_runmgmt.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_runpod_api_delete.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_runpod_key_fingerprint.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_runpod_key_waterfall.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_runpod_slots.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_server_billing.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_server_db.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_sft_gc_off.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_spec_and_validation.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_thinking_config.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_update_check.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_verifiers.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_version.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_vl_warmstart_adapter_keys.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_vl_warmstart_recombine.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_vl_weight_sync.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_wandb_naming.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_warmstart_cross_repo.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_weight_cache.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_worker_dryrun.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_worker_hardexit.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_worker_image.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_worker_init_heartbeat.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_worker_stack.py +0 -0
- {freesolo_flash_dev-0.2.31 → freesolo_flash_dev-0.2.32}/tests/test_worker_thinking.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: freesolo-flash-dev
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.32
|
|
4
4
|
Summary: Flash — managed LoRA post-training (SFT/GRPO) 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
|
|
@@ -51,15 +51,16 @@ Description-Content-Type: text/markdown
|
|
|
51
51
|
|
|
52
52
|
# Flash
|
|
53
53
|
|
|
54
|
-
Managed LoRA post-training service: SFT and GRPO on managed
|
|
55
|
-
The allocator picks the cheapest validated
|
|
54
|
+
Managed LoRA post-training service: SFT and GRPO on managed Flash GPUs.
|
|
55
|
+
The allocator picks the cheapest validated managed GPU class that fits the run.
|
|
56
56
|
|
|
57
57
|
## Scope
|
|
58
58
|
|
|
59
59
|
- `flash train <cfg.toml>` / control-plane `POST /runs` — submit a training job;
|
|
60
60
|
one dedicated GPU per run, supervised server-side (stall watchdog, bounded
|
|
61
61
|
auto-retry resuming from the last streamed checkpoint, endpoint GC).
|
|
62
|
-
- `flash deploy`, `flash chat` — serving for trained adapters
|
|
62
|
+
- `flash checkpoints`, `flash deploy`, `flash chat` — serving for trained adapters,
|
|
63
|
+
including deployable intermediate RL checkpoints.
|
|
63
64
|
- **Freesolo SDK environments.** Every run names a Freesolo environment id.
|
|
64
65
|
Scaffold `environment.py` plus `datasets/train.jsonl`, upload `.` or another
|
|
65
66
|
folder with `flash env push --name <name> <folder>`, then reference the
|
|
@@ -70,21 +71,22 @@ The allocator picks the cheapest validated RunPod GPU class that fits the run.
|
|
|
70
71
|
## Layout
|
|
71
72
|
|
|
72
73
|
- `flash/catalog.py` — curated model catalog (Qwen3 dense supported tier;
|
|
73
|
-
Qwen3.5/3.6 experimental tier)
|
|
74
|
-
|
|
74
|
+
Qwen3.5/3.6 experimental tier), VRAM-fit sizing, and each model's `thinking`
|
|
75
|
+
capability (opt-in reasoning mode `thinking = true`)
|
|
75
76
|
- `flash/schema.py`, `flash/spec.py` — TOML → `JobSpec`
|
|
76
77
|
- `flash/runner.py` — server-side run supervisor (durable job handle,
|
|
77
78
|
retries, cost guard, endpoint GC)
|
|
78
|
-
- `flash/providers/` —
|
|
79
|
+
- `flash/providers/` — managed GPU substrate code (pricing, GPU classes, durable
|
|
79
80
|
submit/poll, preflight) behind the `base.Provider` protocol, with an
|
|
80
|
-
`allocator.py` that picks the cheapest fitting class
|
|
81
|
+
`allocator.py` that picks the cheapest fitting managed GPU class
|
|
81
82
|
- `flash/engine/` — the on-GPU worker (TRL + colocated vLLM rollouts) and the
|
|
82
83
|
shared recipe; SFT targets and RL rewards route through the active environment
|
|
83
84
|
(task-specific grading lives with its example, not in the engine)
|
|
84
85
|
- `flash/envs/` — environment machinery: registry and the adapter that loads
|
|
85
86
|
Freesolo SDK environments onto the worker's interface
|
|
86
87
|
- `flash env setup` — scaffold a starter local Freesolo env, `datasets/train.jsonl`,
|
|
87
|
-
|
|
88
|
+
ready-to-run configs, and a `TRAINING.md` playbook with common failure modes and
|
|
89
|
+
mitigations
|
|
88
90
|
- `flash/serve/`, `flash/server/` — adapter serving and the FastAPI control
|
|
89
91
|
plane (run operator-side via the separate `flash-server` command)
|
|
90
92
|
- `Dockerfile` — the control-plane image (used by the repo docker-compose)
|
|
@@ -101,12 +103,12 @@ uv run flash --help
|
|
|
101
103
|
uv run flash-server # control plane (operator-side, run once)
|
|
102
104
|
```
|
|
103
105
|
|
|
104
|
-
The control plane owns
|
|
105
|
-
plus the shared `HF_TOKEN`.
|
|
106
|
+
The control plane owns infrastructure credentials plus the shared `HF_TOKEN`.
|
|
106
107
|
The artifact repo is platform-managed and environment-scoped (runs for the same environment share
|
|
107
108
|
one private `Freesolo-Co/flashrun-<environment>-<hash>` repo, written by the operator `HF_TOKEN`);
|
|
108
|
-
|
|
109
|
-
|
|
109
|
+
Flash uploads code under content-addressed prefixes and only reuses completed snapshots. The repo is
|
|
110
|
+
not a user knob and not an operator-wide env var. Clients authenticate with their freesolo API key
|
|
111
|
+
(`flash login`).
|
|
110
112
|
|
|
111
113
|
## Release channels
|
|
112
114
|
|
|
@@ -175,10 +177,10 @@ Use `choices[0].message.content` for the generated text. The run id is the adapt
|
|
|
175
177
|
for serving. If the run is not deployed yet, `/v1/runs/<run_id>/chat` returns `409`
|
|
176
178
|
with a hint to deploy first.
|
|
177
179
|
|
|
178
|
-
Operators can also call the
|
|
179
|
-
The default serving
|
|
180
|
-
operators can point Flash at another
|
|
181
|
-
Use that same base URL when calling the
|
|
180
|
+
Operators can also call the serving backend directly after the adapter is registered.
|
|
181
|
+
The default serving backend is `https://clado-ai--freesolo-lora-serving.modal.run`, and
|
|
182
|
+
operators can point Flash at another backend by setting `FREESOLO_SERVING_URL`.
|
|
183
|
+
Use that same base URL when calling the backend directly; pass the run id as `model`:
|
|
182
184
|
|
|
183
185
|
```bash
|
|
184
186
|
export FREESOLO_SERVING_URL=https://clado-ai--freesolo-lora-serving.modal.run
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
# Flash
|
|
2
2
|
|
|
3
|
-
Managed LoRA post-training service: SFT and GRPO on managed
|
|
4
|
-
The allocator picks the cheapest validated
|
|
3
|
+
Managed LoRA post-training service: SFT and GRPO on managed Flash GPUs.
|
|
4
|
+
The allocator picks the cheapest validated managed GPU class that fits the run.
|
|
5
5
|
|
|
6
6
|
## Scope
|
|
7
7
|
|
|
8
8
|
- `flash train <cfg.toml>` / control-plane `POST /runs` — submit a training job;
|
|
9
9
|
one dedicated GPU per run, supervised server-side (stall watchdog, bounded
|
|
10
10
|
auto-retry resuming from the last streamed checkpoint, endpoint GC).
|
|
11
|
-
- `flash deploy`, `flash chat` — serving for trained adapters
|
|
11
|
+
- `flash checkpoints`, `flash deploy`, `flash chat` — serving for trained adapters,
|
|
12
|
+
including deployable intermediate RL checkpoints.
|
|
12
13
|
- **Freesolo SDK environments.** Every run names a Freesolo environment id.
|
|
13
14
|
Scaffold `environment.py` plus `datasets/train.jsonl`, upload `.` or another
|
|
14
15
|
folder with `flash env push --name <name> <folder>`, then reference the
|
|
@@ -19,21 +20,22 @@ The allocator picks the cheapest validated RunPod GPU class that fits the run.
|
|
|
19
20
|
## Layout
|
|
20
21
|
|
|
21
22
|
- `flash/catalog.py` — curated model catalog (Qwen3 dense supported tier;
|
|
22
|
-
Qwen3.5/3.6 experimental tier)
|
|
23
|
-
|
|
23
|
+
Qwen3.5/3.6 experimental tier), VRAM-fit sizing, and each model's `thinking`
|
|
24
|
+
capability (opt-in reasoning mode `thinking = true`)
|
|
24
25
|
- `flash/schema.py`, `flash/spec.py` — TOML → `JobSpec`
|
|
25
26
|
- `flash/runner.py` — server-side run supervisor (durable job handle,
|
|
26
27
|
retries, cost guard, endpoint GC)
|
|
27
|
-
- `flash/providers/` —
|
|
28
|
+
- `flash/providers/` — managed GPU substrate code (pricing, GPU classes, durable
|
|
28
29
|
submit/poll, preflight) behind the `base.Provider` protocol, with an
|
|
29
|
-
`allocator.py` that picks the cheapest fitting class
|
|
30
|
+
`allocator.py` that picks the cheapest fitting managed GPU class
|
|
30
31
|
- `flash/engine/` — the on-GPU worker (TRL + colocated vLLM rollouts) and the
|
|
31
32
|
shared recipe; SFT targets and RL rewards route through the active environment
|
|
32
33
|
(task-specific grading lives with its example, not in the engine)
|
|
33
34
|
- `flash/envs/` — environment machinery: registry and the adapter that loads
|
|
34
35
|
Freesolo SDK environments onto the worker's interface
|
|
35
36
|
- `flash env setup` — scaffold a starter local Freesolo env, `datasets/train.jsonl`,
|
|
36
|
-
|
|
37
|
+
ready-to-run configs, and a `TRAINING.md` playbook with common failure modes and
|
|
38
|
+
mitigations
|
|
37
39
|
- `flash/serve/`, `flash/server/` — adapter serving and the FastAPI control
|
|
38
40
|
plane (run operator-side via the separate `flash-server` command)
|
|
39
41
|
- `Dockerfile` — the control-plane image (used by the repo docker-compose)
|
|
@@ -50,12 +52,12 @@ uv run flash --help
|
|
|
50
52
|
uv run flash-server # control plane (operator-side, run once)
|
|
51
53
|
```
|
|
52
54
|
|
|
53
|
-
The control plane owns
|
|
54
|
-
plus the shared `HF_TOKEN`.
|
|
55
|
+
The control plane owns infrastructure credentials plus the shared `HF_TOKEN`.
|
|
55
56
|
The artifact repo is platform-managed and environment-scoped (runs for the same environment share
|
|
56
57
|
one private `Freesolo-Co/flashrun-<environment>-<hash>` repo, written by the operator `HF_TOKEN`);
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
Flash uploads code under content-addressed prefixes and only reuses completed snapshots. The repo is
|
|
59
|
+
not a user knob and not an operator-wide env var. Clients authenticate with their freesolo API key
|
|
60
|
+
(`flash login`).
|
|
59
61
|
|
|
60
62
|
## Release channels
|
|
61
63
|
|
|
@@ -124,10 +126,10 @@ Use `choices[0].message.content` for the generated text. The run id is the adapt
|
|
|
124
126
|
for serving. If the run is not deployed yet, `/v1/runs/<run_id>/chat` returns `409`
|
|
125
127
|
with a hint to deploy first.
|
|
126
128
|
|
|
127
|
-
Operators can also call the
|
|
128
|
-
The default serving
|
|
129
|
-
operators can point Flash at another
|
|
130
|
-
Use that same base URL when calling the
|
|
129
|
+
Operators can also call the serving backend directly after the adapter is registered.
|
|
130
|
+
The default serving backend is `https://clado-ai--freesolo-lora-serving.modal.run`, and
|
|
131
|
+
operators can point Flash at another backend by setting `FREESOLO_SERVING_URL`.
|
|
132
|
+
Use that same base URL when calling the backend directly; pass the run id as `model`:
|
|
131
133
|
|
|
132
134
|
```bash
|
|
133
135
|
export FREESOLO_SERVING_URL=https://clado-ai--freesolo-lora-serving.modal.run
|
|
@@ -66,7 +66,7 @@ _HELP_GROUPS: list[tuple[str, list[tuple[str, str]]]] = [
|
|
|
66
66
|
"catalog",
|
|
67
67
|
[
|
|
68
68
|
("models", "list supported base models"),
|
|
69
|
-
("gpus", "list managed GPU classes with
|
|
69
|
+
("gpus", "list managed GPU classes with estimated $/hr"),
|
|
70
70
|
],
|
|
71
71
|
),
|
|
72
72
|
(
|
|
@@ -226,7 +226,7 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
226
226
|
models = sub.add_parser("models", help="list supported base models")
|
|
227
227
|
models.set_defaults(func=cmd_models)
|
|
228
228
|
|
|
229
|
-
gpus = sub.add_parser("gpus", help="list managed GPU classes with
|
|
229
|
+
gpus = sub.add_parser("gpus", help="list managed GPU classes with estimated $/hr")
|
|
230
230
|
gpus.set_defaults(func=cmd_gpus)
|
|
231
231
|
|
|
232
232
|
env = sub.add_parser("env", help="manage Freesolo environments")
|
|
@@ -268,7 +268,9 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
268
268
|
env_pull.set_defaults(func=cmd_env_pull)
|
|
269
269
|
|
|
270
270
|
env_delete = env_sub.add_parser("delete", help="delete a published Freesolo environment")
|
|
271
|
-
env_delete.add_argument(
|
|
271
|
+
env_delete.add_argument(
|
|
272
|
+
"env_id", help="the Freesolo environment id to delete, e.g. you/your-env"
|
|
273
|
+
)
|
|
272
274
|
env_delete.add_argument(
|
|
273
275
|
"-y",
|
|
274
276
|
"--yes",
|
|
@@ -236,8 +236,8 @@ def cmd_env_setup(args) -> int:
|
|
|
236
236
|
"[train]\n"
|
|
237
237
|
"steps = 150\n"
|
|
238
238
|
"lora_rank = 32\n"
|
|
239
|
-
"# GPU and
|
|
240
|
-
"# the cheapest fitting class
|
|
239
|
+
"# GPU and HF artifacts are managed automatically by the platform: the GPU is\n"
|
|
240
|
+
"# the cheapest fitting managed class, and artifacts live in a private environment-scoped repo.\n"
|
|
241
241
|
)
|
|
242
242
|
sft = Path("configs/sft.toml")
|
|
243
243
|
if not sft.exists():
|
|
@@ -248,8 +248,8 @@ def cmd_env_setup(args) -> int:
|
|
|
248
248
|
"[train]\n"
|
|
249
249
|
"epochs = 1\n"
|
|
250
250
|
"lora_rank = 32\n"
|
|
251
|
-
"# GPU and
|
|
252
|
-
"# the cheapest fitting class
|
|
251
|
+
"# GPU and HF artifacts are managed automatically by the platform: the GPU is\n"
|
|
252
|
+
"# the cheapest fitting managed class, and artifacts live in a private environment-scoped repo.\n"
|
|
253
253
|
)
|
|
254
254
|
training = Path("TRAINING.md")
|
|
255
255
|
if not training.exists():
|
|
@@ -280,7 +280,7 @@ def cmd_models(args) -> int:
|
|
|
280
280
|
|
|
281
281
|
|
|
282
282
|
def cmd_gpus(args) -> int:
|
|
283
|
-
"""List
|
|
283
|
+
"""List validated managed GPU classes, VRAM, and estimated $/hr."""
|
|
284
284
|
from flash.providers.base import GPU_INFO
|
|
285
285
|
from flash.providers.runpod.pricing import static_rates as runpod_static_rates
|
|
286
286
|
|
|
@@ -290,7 +290,7 @@ def cmd_gpus(args) -> int:
|
|
|
290
290
|
)
|
|
291
291
|
tip = (
|
|
292
292
|
"Tip: GPU class selection is fully automatic — the submit-time allocator always picks the\n"
|
|
293
|
-
"cheapest validated
|
|
293
|
+
"cheapest validated managed class that fits the model, so you don't pin a GPU type."
|
|
294
294
|
)
|
|
295
295
|
if render.styled():
|
|
296
296
|
rows = [(info.name, info.vram_gb, runpod_rates.get(info.name)) for info in infos]
|
|
@@ -300,7 +300,7 @@ def cmd_gpus(args) -> int:
|
|
|
300
300
|
def fmt_rate(v: float | None) -> str:
|
|
301
301
|
return f"{v:>10.2f}" if v else f"{'-':>10}"
|
|
302
302
|
|
|
303
|
-
print(f"{'gpu':<16}{'vram':>6}{'
|
|
303
|
+
print(f"{'gpu':<16}{'vram':>6}{'$/hr':>11}")
|
|
304
304
|
for info in infos:
|
|
305
305
|
runpod_rate = runpod_rates.get(info.name)
|
|
306
306
|
print(f"{info.name:<16}{info.vram_gb:>5}G{fmt_rate(runpod_rate):>11}")
|
|
@@ -483,8 +483,7 @@ def cmd_runs(args) -> int:
|
|
|
483
483
|
spec = r.get("spec") or {}
|
|
484
484
|
model = spec.get("model", "")
|
|
485
485
|
algorithm = str(spec.get("algorithm") or "-").upper()
|
|
486
|
-
|
|
487
|
-
where = f"{gpu}@{provider}" if provider else gpu
|
|
486
|
+
where = render._run_gpu(spec, r.get("remote") or {})
|
|
488
487
|
print(
|
|
489
488
|
f"{r['run_id']:<32} {r['state']:<11} {algorithm:<5} "
|
|
490
489
|
f"{r.get('cost_usd', 0.0):>8.4f} {where:<22} {model}"
|
|
@@ -357,6 +357,21 @@ def _color_json(obj, depth: int) -> str:
|
|
|
357
357
|
return _paint(json.dumps(obj), _GREEN)
|
|
358
358
|
|
|
359
359
|
|
|
360
|
+
def _hide_provider_metadata(obj):
|
|
361
|
+
"""Styled CLI details are for humans; keep backend provider names out of that view."""
|
|
362
|
+
if isinstance(obj, dict):
|
|
363
|
+
return {
|
|
364
|
+
k: _hide_provider_metadata(v)
|
|
365
|
+
for k, v in obj.items()
|
|
366
|
+
if k.lower() not in {"provider", "flash_arm"}
|
|
367
|
+
}
|
|
368
|
+
if isinstance(obj, list):
|
|
369
|
+
return [_hide_provider_metadata(v) for v in obj]
|
|
370
|
+
if isinstance(obj, str) and obj.lower() in {"runpod", "lambda"}:
|
|
371
|
+
return "managed"
|
|
372
|
+
return obj
|
|
373
|
+
|
|
374
|
+
|
|
360
375
|
def version(value: str) -> str:
|
|
361
376
|
"""The wordmark + version."""
|
|
362
377
|
mark = _paint(CLI_NAME, _ACCENT, "1")
|
|
@@ -388,13 +403,9 @@ def gpus_table(rows: list[tuple[str, int, float | None]], tip: str) -> str:
|
|
|
388
403
|
return _safe(f"{header('gpus', 'managed GPU classes')}\n{table}\n\n{_dim(tip)}")
|
|
389
404
|
|
|
390
405
|
|
|
391
|
-
def
|
|
392
|
-
"""
|
|
393
|
-
|
|
394
|
-
"runpod" if remote else (spec.get("gpu") or {}).get("provider", "")
|
|
395
|
-
)
|
|
396
|
-
gpu = remote.get("gpu") or (spec.get("gpu") or {}).get("type", "")
|
|
397
|
-
return gpu, provider
|
|
406
|
+
def _run_gpu(spec: dict, remote: dict) -> str:
|
|
407
|
+
"""Human-facing GPU label. Provider metadata stays internal."""
|
|
408
|
+
return remote.get("gpu") or (spec.get("gpu") or {}).get("type", "")
|
|
398
409
|
|
|
399
410
|
|
|
400
411
|
def runs_table(runs: list[dict]) -> str:
|
|
@@ -404,8 +415,7 @@ def runs_table(runs: list[dict]) -> str:
|
|
|
404
415
|
spec = r.get("spec") or {}
|
|
405
416
|
model = spec.get("model", "")
|
|
406
417
|
algorithm = str(spec.get("algorithm") or "-").upper()
|
|
407
|
-
|
|
408
|
-
where = f"{gpu}@{provider}" if provider else gpu
|
|
418
|
+
where = _run_gpu(spec, r.get("remote") or {})
|
|
409
419
|
color, uni, ascii_dot = _STATE_STYLE.get(str(r.get("state", "")).lower(), (_GRAY, "•", "-"))
|
|
410
420
|
body.append(
|
|
411
421
|
[
|
|
@@ -471,8 +481,7 @@ def _humanize_ts(value) -> str | None:
|
|
|
471
481
|
def run_status(obj: dict) -> str:
|
|
472
482
|
"""A curated status panel for `flash status`, with the full JSON below for completeness."""
|
|
473
483
|
spec = obj.get("spec") or {}
|
|
474
|
-
|
|
475
|
-
where = f"{gpu} @ {provider}" if gpu and provider else (gpu or None)
|
|
484
|
+
where = _run_gpu(spec, obj.get("remote") or {}) or None
|
|
476
485
|
pairs = [
|
|
477
486
|
("run id", _paint(obj.get("run_id", ""), _ACCENT2)),
|
|
478
487
|
("model", spec.get("model")),
|
|
@@ -492,7 +501,7 @@ def run_status(obj: dict) -> str:
|
|
|
492
501
|
if obj.get("error"):
|
|
493
502
|
pairs.append(("error", _paint(str(obj["error"]), _RED)))
|
|
494
503
|
head = f"{header('status')}\n {badge(obj.get('state', 'unknown'))}\n\n{_kv(pairs)}"
|
|
495
|
-
raw = f"{_dim('details')}\n{_json(obj)}"
|
|
504
|
+
raw = f"{_dim('details')}\n{_json(_hide_provider_metadata(obj))}"
|
|
496
505
|
return _safe(f"{head}\n\n{raw}")
|
|
497
506
|
|
|
498
507
|
|
|
@@ -509,7 +518,7 @@ def object_panel(cmd: str, obj: dict, desc: str | None = None) -> str:
|
|
|
509
518
|
if rid:
|
|
510
519
|
line += " " + _paint(rid, _ACCENT2)
|
|
511
520
|
parts.append(line + "\n")
|
|
512
|
-
parts.append(_json(obj))
|
|
521
|
+
parts.append(_json(_hide_provider_metadata(obj)))
|
|
513
522
|
return _safe("\n".join(parts))
|
|
514
523
|
|
|
515
524
|
|
|
@@ -586,7 +595,7 @@ def cost_panel(est) -> str:
|
|
|
586
595
|
),
|
|
587
596
|
(
|
|
588
597
|
"gpu",
|
|
589
|
-
f"{est.gpu}
|
|
598
|
+
f"{est.gpu} "
|
|
590
599
|
f"{_dim(f'({est.gpu_vram_gb} GB; needs >= {est.required_vram_gb} GB)')} "
|
|
591
600
|
f"@ {money(est.gpu_hourly_usd, 2)}/hr",
|
|
592
601
|
),
|
|
@@ -20,10 +20,10 @@ actually produced, and decide — honestly — whether the model got better.
|
|
|
20
20
|
|
|
21
21
|
Flash is a **managed** training service with a thin CLI/client. You author an
|
|
22
22
|
environment (the task + its reward), publish it, and submit SFT or GRPO runs from a
|
|
23
|
-
TOML config. Flash allocates the cheapest fitting GPU
|
|
24
|
-
streams logs back, and serves the result. You never handle
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
TOML config. Flash allocates the cheapest fitting managed GPU class, runs the job,
|
|
24
|
+
streams logs back, and serves the result. You never handle infrastructure credentials —
|
|
25
|
+
you authenticate once with a freesolo API key, and everything below is a `flash` CLI
|
|
26
|
+
command.
|
|
27
27
|
|
|
28
28
|
### Install & authenticate
|
|
29
29
|
|
|
@@ -32,7 +32,7 @@ pip install freesolo-flash # installs the `flash` CLI (import name is a
|
|
|
32
32
|
flash login --api-key fslo_... # or: export FREESOLO_API_KEY=fslo_... (create a key at https://freesolo.co)
|
|
33
33
|
flash whoami # confirm the identity behind your key
|
|
34
34
|
flash models # supported base models (and which support `thinking`)
|
|
35
|
-
flash gpus # managed GPU classes with
|
|
35
|
+
flash gpus # managed GPU classes with estimated $/hr
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
### The project layout (`flash env setup` created this)
|
|
@@ -105,12 +105,14 @@ id = "your-org/my-env" # the id printed by `flash env push`
|
|
|
105
105
|
steps = 150 # GRPO is step-driven; SFT is epoch-driven (epochs = N)
|
|
106
106
|
lora_rank = 32
|
|
107
107
|
lora_alpha = 64
|
|
108
|
+
# All GRPO/SFT knobs live under [train]. Do not add [grpo] or [sft] tables.
|
|
108
109
|
```
|
|
109
110
|
|
|
110
|
-
GPU and
|
|
111
|
-
picks the cheapest class that fits,
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
GPU and HF artifacts are **fully managed** — do not pick `gpu.type` or set
|
|
112
|
+
`train.hf_repo`; the allocator picks the cheapest validated managed GPU class that fits,
|
|
113
|
+
and run artifacts are stored in a private environment-scoped repo with content-addressed
|
|
114
|
+
Flash code snapshots. Compose or tweak configs without editing files: `--config
|
|
115
|
+
extra.toml` (deep-merge) and `--set key=value` (e.g. `--set train.steps=300`).
|
|
114
116
|
|
|
115
117
|
### 4. Submit
|
|
116
118
|
|
|
@@ -139,6 +141,7 @@ flash deploy <run-id> # serve the trained adapter (--step N for an in
|
|
|
139
141
|
flash chat <run-id> -m "hello" # chat with the deployed adapter
|
|
140
142
|
flash deployments # active serving endpoints
|
|
141
143
|
flash undeploy <run-id> # tear the endpoint down
|
|
144
|
+
flash export --adapter-id <run-id> --repository <you>/<repo> # copy adapter weights to your HF repo
|
|
142
145
|
```
|
|
143
146
|
|
|
144
147
|
The rest of this file is about doing the above *well* — designing a reward that teaches,
|
|
@@ -185,6 +188,32 @@ If any box is unchecked, the run is not done improving — keep training, don't
|
|
|
185
188
|
|
|
186
189
|
---
|
|
187
190
|
|
|
191
|
+
## Common Flash issues and mitigations
|
|
192
|
+
|
|
193
|
+
Most bad Flash runs fail in a small number of predictable ways. Check these before
|
|
194
|
+
spending another GPU run:
|
|
195
|
+
|
|
196
|
+
| Issue | Symptom | Mitigation |
|
|
197
|
+
| --- | --- | --- |
|
|
198
|
+
| Environment id is blank or stale | `flash train --dry-run` fails, or the worker uses old reward/data | Run `flash env push --name my-env .` after every environment/data edit and paste the returned id into every config you submit. |
|
|
199
|
+
| Local-only env path in config | Config validation says there is no local path mode | Publish first, then use the returned slug in `[environment] id`. `flash train` only runs published env ids, not local paths. |
|
|
200
|
+
| Config knobs are in the wrong table | Validation rejects `[grpo]`, `[sft]`, or unknown `[train]` keys | Put `steps`, `epochs`, `group_size`, `max_tokens`, `temperature`, `max_length`, LoRA, and other training knobs under `[train]`. |
|
|
201
|
+
| Trying to pin managed infrastructure | `gpu.type`, `train.hf_repo`, or `model_policy` changes do not do what you expected | Treat GPU choice, model policy, and the run artifact repo as managed. Tune the model, algorithm, environment, and `[train]` knobs instead. |
|
|
202
|
+
| Secrets are not available on the worker | Reward code works locally but remote logs show missing API keys or auth failures | List secret names under `[environment] secrets = [...]`, export those env vars locally before submit, or put them in local `.env` / `.env.local`. Never put secret values in `[worker_env]` or hard-code them in the config. |
|
|
203
|
+
| Wrong model / thinking setting | Config validation fails, or chat behavior does not match the run | Use `flash models`; set `thinking = true` only for supported models. Thinking is a training-time/run-level choice and serving preserves that parity, so `flash chat` does not expose an override flag. |
|
|
204
|
+
| Thinking reward grades the wrong text | Rewards accidentally score hidden reasoning, or ignore reasoning you meant to inspect | By default, score the answer text. In thinking mode the response object is still string-compatible, but also exposes `.completion`, `.thinking`, and `.raw` when a reward intentionally needs those fields. |
|
|
205
|
+
| All-zero or flat GRPO reward | `reward_mean` stays near 0 and outputs do not improve | Make the reward dense: give partial credit for parse/format/execution/correctness tiers, and log a separate clean `success` metric. Do not keep rerunning an all-zero reward. |
|
|
206
|
+
| Reward rises but behavior is worse | Short, templated, malformed, or reward-hacked outputs score well | Deploy the adapter and probe real examples. Add hard validity gates before judge calls, penalize degenerate shortcuts, and judge the outcome rather than the surface string. |
|
|
207
|
+
| Output is truncated | Correct-looking answers cut off mid-response or JSON is incomplete | Increase `max_tokens` for GRPO rollouts or `max_length` for SFT only after seeing truncation. Oversizing them by default just burns memory/cost. |
|
|
208
|
+
| Infrastructure, CUDA, OOM, vLLM, or kernel failure | Run errors before useful metrics, often during setup/model load | Treat this as infrastructure pressure, not proof the model is too large. Read `flash status <run-id> --logs`, reduce footprint (`max_length`, `max_tokens`, `group_size`) if needed, and let Flash retry/allocate another fitting GPU class. |
|
|
209
|
+
| Run looks stuck after disconnecting | Terminal stopped streaming but the job may still be alive | Ctrl-C detaches. Use `flash status <run-id> --follow` to reattach, `--logs` for the console/error tail, or `flash cancel <run-id>` if you intentionally want to stop it. |
|
|
210
|
+
| Final checkpoint regresses | Last step is worse than an earlier checkpoint | Run `flash checkpoints <run-id>`, deploy a specific step with `flash deploy <run-id> --step N`, and compare with held-out probes before exporting or relying on the final adapter. |
|
|
211
|
+
| Export fails before upload | CLI says no HuggingFace token | Pass `flash export --api-key hf_...`, or set `HF_TOKEN` in your shell, `.env`, or `.env.local`. Exports are private unless you pass `--public`. |
|
|
212
|
+
| SFT loss improves but quality does not | Train loss falls while held-out behavior stalls or degrades | Keep a held-out split outside training. Deploy and score that split; if quality drops, reduce epochs or improve data instead of adding more passes. |
|
|
213
|
+
| Cost surprises | A quick experiment uses more GPU time than intended | Start with `--dry-run` and `--cost`, cap steps/epochs for smoke tests, and scale only after reward/data wiring is proven. Setup time is reported for observability; customer cost is based on training-loop GPU time. |
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
188
217
|
## Judge the run, don't just finish it
|
|
189
218
|
|
|
190
219
|
- **Judge the trend, not a single number.** The proof of training is the curve:
|
|
@@ -259,6 +288,12 @@ attach is logged by name in the per-scorer breakdown — that is how the clean s
|
|
|
259
288
|
rate becomes visible. Use the shaped `score` to confirm the model is learning *at all*,
|
|
260
289
|
and judge the run on the explicit `success` metric.
|
|
261
290
|
|
|
291
|
+
When `thinking = true`, score the final answer unless you intentionally need the
|
|
292
|
+
reasoning trace. Flash passes a string-compatible response object to `score_response`;
|
|
293
|
+
`str(response_text)` is the answer text, while `response_text.completion`,
|
|
294
|
+
`response_text.thinking`, and `response_text.raw` are available for rewards that
|
|
295
|
+
explicitly inspect the separated completion, reasoning, or original raw model output.
|
|
296
|
+
|
|
262
297
|
### Reward rules that prevent silent failure
|
|
263
298
|
|
|
264
299
|
- **Return `0.0` explicitly — never let scoring raise.** An uncaught exception in
|
|
@@ -418,7 +453,7 @@ on a beyond-noise improvement.
|
|
|
418
453
|
|
|
419
454
|
## Treat crashes as infra, not model size
|
|
420
455
|
|
|
421
|
-
> A CUDA / OOM / vLLM / kernel /
|
|
456
|
+
> A CUDA / OOM / vLLM / kernel / infrastructure error is an **infrastructure** problem, not a
|
|
422
457
|
> sign the model is too big. Lower `max_length`, `max_tokens`, or `group_size` to shrink
|
|
423
458
|
> the run's footprint and let the allocator retry onto the next fitting GPU class — do
|
|
424
459
|
> **not** switch to a smaller model to make a crash disappear. That silently destroys
|
|
@@ -431,6 +466,8 @@ on a beyond-noise improvement.
|
|
|
431
466
|
```bash
|
|
432
467
|
flash env setup # scaffold environment.py, datasets/, configs/, this file
|
|
433
468
|
flash env push --name my-env . # publish the environment; paste the returned id into [environment]
|
|
469
|
+
flash env pull your-org/my-env # download a published environment into the current folder
|
|
470
|
+
flash env delete your-org/my-env -y # delete a published environment
|
|
434
471
|
flash train configs/rl.toml --dry-run # validate the config locally (no GPU, no charge)
|
|
435
472
|
flash train configs/rl.toml --cost # pre-flight USD estimate, then exit
|
|
436
473
|
flash train configs/rl.toml # submit and follow logs (Ctrl-C detaches; --background to skip following)
|
|
@@ -438,7 +475,15 @@ flash status <run-id> # state + accrued cost
|
|
|
438
475
|
flash status <run-id> --logs # reward/loss trend + worker console/error logs
|
|
439
476
|
flash status <run-id> --follow # stream a live run to completion
|
|
440
477
|
flash runs # list your runs and their state/cost
|
|
478
|
+
flash cancel <run-id> # stop a live run
|
|
479
|
+
flash checkpoints <run-id> # list deployable RL checkpoints
|
|
441
480
|
flash deploy <run-id> # serve the trained adapter
|
|
481
|
+
flash deploy <run-id> --step N # serve a specific RL checkpoint
|
|
482
|
+
flash chat <run-id> -m "probe" # stream a reply from the deployed adapter
|
|
483
|
+
flash deployments # list active serving deployments
|
|
484
|
+
flash undeploy <run-id> # tear down an active deployment
|
|
485
|
+
flash export --adapter-id <run-id> --repository <you>/<repo> # export final adapter
|
|
486
|
+
flash export --adapter-id <run-id> --repository <you>/<repo> --step N # export a checkpoint
|
|
442
487
|
```
|
|
443
488
|
|
|
444
489
|
See the full reference at https://freesolo.co/docs.
|
|
@@ -41,7 +41,9 @@ class RunConfig:
|
|
|
41
41
|
# substrate up front (else it filters out every candidate -> confusing "no GPU fits").
|
|
42
42
|
prov = (self.provider or "auto").strip().lower() or "auto"
|
|
43
43
|
if prov not in ("auto", *PROVIDER_NAMES):
|
|
44
|
-
raise ValueError(
|
|
44
|
+
raise ValueError(
|
|
45
|
+
f"unknown provider {self.provider!r} (auto, {', '.join(PROVIDER_NAMES)})"
|
|
46
|
+
)
|
|
45
47
|
object.__setattr__(self, "provider", prov)
|
|
46
48
|
if self.steps < 1:
|
|
47
49
|
raise ValueError(f"steps must be >= 1, got {self.steps}")
|
|
@@ -86,7 +88,14 @@ class RunConfig:
|
|
|
86
88
|
comp = None
|
|
87
89
|
batch = self.batch_size if self.batch_size is not None else RECIPE.sft.effective_batch
|
|
88
90
|
group = None
|
|
89
|
-
return replace(
|
|
91
|
+
return replace(
|
|
92
|
+
self,
|
|
93
|
+
seq_len=seq,
|
|
94
|
+
completion_len=comp,
|
|
95
|
+
batch_size=batch,
|
|
96
|
+
group_size=group,
|
|
97
|
+
lora_rank=lora,
|
|
98
|
+
)
|
|
90
99
|
|
|
91
100
|
def train_knobs(self) -> dict[str, int]:
|
|
92
101
|
"""The knob dict ``model_required_vram_gb`` consumes. Only an EXPLICIT batch_size is
|
|
@@ -141,7 +150,7 @@ class CostEstimate:
|
|
|
141
150
|
"""Multi-line itemized breakdown for CLI output."""
|
|
142
151
|
lines = [
|
|
143
152
|
f"Run : {self.model_id} [{self.method.upper()}, {self.steps} steps]",
|
|
144
|
-
f"GPU : {self.gpu}
|
|
153
|
+
f"GPU : {self.gpu} "
|
|
145
154
|
f"({self.gpu_vram_gb} GB; run needs >= {self.required_vram_gb} GB) "
|
|
146
155
|
f"@ ${self.gpu_hourly_usd:.2f}/hr",
|
|
147
156
|
f"Setup : {self.setup_seconds / 60:.1f} min (cold start: boot + deps + model load"
|
|
@@ -410,6 +410,45 @@ def _github_response_message(payload: object) -> str:
|
|
|
410
410
|
return ""
|
|
411
411
|
|
|
412
412
|
|
|
413
|
+
def _github_tree_entries(ref: GitHubEnvironmentRef, treeish: str, context: str) -> list[dict]:
|
|
414
|
+
payload = _download_github_json(ref, _github_tree_url(ref, treeish), context)
|
|
415
|
+
if not isinstance(payload, dict) or not isinstance(payload.get("tree"), list):
|
|
416
|
+
raise RuntimeError(
|
|
417
|
+
f"GitHub path {context!r} is not an environment directory"
|
|
418
|
+
f"{_github_response_message(payload)}"
|
|
419
|
+
)
|
|
420
|
+
if payload.get("truncated"):
|
|
421
|
+
raise RuntimeError(
|
|
422
|
+
f"GitHub tree response for environment directory {context!r} was truncated"
|
|
423
|
+
)
|
|
424
|
+
entries = payload["tree"]
|
|
425
|
+
if not all(isinstance(entry, dict) for entry in entries):
|
|
426
|
+
raise RuntimeError("GitHub tree response included an invalid entry")
|
|
427
|
+
return entries
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def _resolve_github_directory_tree_sha(ref: GitHubEnvironmentRef, repo_dir: str) -> str:
|
|
431
|
+
treeish = ref.ref
|
|
432
|
+
current = ""
|
|
433
|
+
for part in [part for part in repo_dir.split("/") if part]:
|
|
434
|
+
entries = _github_tree_entries(ref, treeish, current or ref.ref)
|
|
435
|
+
match = next(
|
|
436
|
+
(
|
|
437
|
+
entry
|
|
438
|
+
for entry in entries
|
|
439
|
+
if entry.get("path") == part
|
|
440
|
+
and entry.get("type") == "tree"
|
|
441
|
+
and isinstance(entry.get("sha"), str)
|
|
442
|
+
),
|
|
443
|
+
None,
|
|
444
|
+
)
|
|
445
|
+
current = f"{current}/{part}" if current else part
|
|
446
|
+
if match is None:
|
|
447
|
+
raise RuntimeError(f"GitHub path {repo_dir!r} is not an environment directory")
|
|
448
|
+
treeish = match["sha"]
|
|
449
|
+
return treeish
|
|
450
|
+
|
|
451
|
+
|
|
413
452
|
def _download_github_directory(ref: GitHubEnvironmentRef, repo_dir: str, dest: Path) -> Path:
|
|
414
453
|
"""Download one GitHub directory into a repo-shaped tree under ``dest``."""
|
|
415
454
|
repo_root = dest / "repo"
|
|
@@ -464,9 +503,10 @@ def _download_github_directory(ref: GitHubEnvironmentRef, repo_dir: str, dest: P
|
|
|
464
503
|
(repo_root / path).mkdir(parents=True, exist_ok=True)
|
|
465
504
|
|
|
466
505
|
create_dir(repo_dir)
|
|
506
|
+
package_tree_sha = _resolve_github_directory_tree_sha(ref, repo_dir)
|
|
467
507
|
payload = _download_github_json(
|
|
468
508
|
ref,
|
|
469
|
-
_github_tree_url(ref,
|
|
509
|
+
_github_tree_url(ref, package_tree_sha, recursive=True),
|
|
470
510
|
repo_dir,
|
|
471
511
|
)
|
|
472
512
|
if not isinstance(payload, dict) or not isinstance(payload.get("tree"), list):
|