freesolo-flash-dev 0.2.25__tar.gz → 0.2.27__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.25 → freesolo_flash_dev-0.2.27}/.env.example +5 -0
- freesolo_flash_dev-0.2.27/.github/workflows/auto-rebake.yml +245 -0
- freesolo_flash_dev-0.2.27/.github/workflows/bake-kernel-cache.yml +245 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/.github/workflows/publish-dev.yml +17 -3
- freesolo_flash_dev-0.2.27/.github/workflows/version-parity.yml +49 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/.github/workflows/worker-image.yml +37 -3
- freesolo_flash_dev-0.2.27/Dockerfile +42 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/Dockerfile.worker +3 -3
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/PKG-INFO +9 -6
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/README.md +8 -5
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/docker/Dockerfile.kernelcache +10 -0
- freesolo_flash_dev-0.2.27/docker/Dockerfile.kernelcache.relayer +27 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/docker/bake_kernel_cache.py +16 -2
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/docker/bake_pod_entry.py +5 -1
- freesolo_flash_dev-0.2.27/docker/kernel_fingerprint.py +245 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/docs/cli-style/README.md +2 -2
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/docs/cli-style/generate.py +4 -4
- freesolo_flash_dev-0.2.27/docs/cli-theme/README.md +32 -0
- freesolo_flash_dev-0.2.27/docs/cli-theme/before-after-dark.png +0 -0
- freesolo_flash_dev-0.2.27/docs/cli-theme/before-after-light.png +0 -0
- freesolo_flash_dev-0.2.27/docs/cli-theme/gallery-dark.png +0 -0
- freesolo_flash_dev-0.2.27/docs/cli-theme/gallery-light.png +0 -0
- freesolo_flash_dev-0.2.27/docs/cli-theme/help-dark.png +0 -0
- freesolo_flash_dev-0.2.27/docs/cli-theme/help-light.png +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/__init__.py +1 -1
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/_channel.py +7 -2
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/catalog.py +73 -2
- {freesolo_flash_dev-0.2.25/flash/cli/main → freesolo_flash_dev-0.2.27/flash/cli}/__init__.py +136 -11
- {freesolo_flash_dev-0.2.25/flash/cli/main → freesolo_flash_dev-0.2.27/flash/cli}/__main__.py +1 -1
- {freesolo_flash_dev-0.2.25/flash/cli/main → freesolo_flash_dev-0.2.27/flash/cli}/commands.py +64 -15
- {freesolo_flash_dev-0.2.25/flash/cli/main → freesolo_flash_dev-0.2.27/flash/cli}/envpush.py +21 -22
- {freesolo_flash_dev-0.2.25/flash/cli/main → freesolo_flash_dev-0.2.27/flash/cli}/render.py +162 -10
- {freesolo_flash_dev-0.2.25/flash/cli/main → freesolo_flash_dev-0.2.27/flash/cli}/training_doc.py +0 -3
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/client/__init__.py +1 -1
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/client/config.py +1 -1
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/client/http.py +34 -5
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/client/runtime_secrets.py +23 -6
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/cost/analytical.py +4 -1
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/cost/facts.py +24 -11
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/cost/spec.py +0 -1
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/engine/accounting.py +0 -2
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/engine/chalk_kernels.py +1 -6
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/engine/recipe.py +0 -12
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/engine/vram.py +42 -9
- freesolo_flash_dev-0.2.27/flash/engine/worker/__init__.py +599 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/_pkg.py +30 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/adapter.py +287 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/decoding.py +144 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/finalize.py +68 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/gpu_setup.py +101 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/grpo.py +297 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/heartbeat.py +322 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/hf.py +563 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/engine/worker/kernel_warmup.py +23 -18
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/engine/worker/lora.py +253 -66
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/engine/worker/packing.py +127 -7
- freesolo_flash_dev-0.2.27/flash/engine/worker/perf/__init__.py +495 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/perf/attn.py +176 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/perf/diagnostics.py +240 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/perf/lifecycle.py +274 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/perf/liger.py +73 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/perf/loraplus.py +40 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/perf/memory.py +84 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/rl.py +930 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/sft.py +683 -0
- freesolo_flash_dev-0.2.27/flash/engine/worker/wandb_log.py +145 -0
- freesolo_flash_dev-0.2.25/flash/envs/adapter/__init__.py → freesolo_flash_dev-0.2.27/flash/envs/adapter.py +55 -10
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/__init__.py +5 -10
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/_auth.py +0 -8
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/_http.py +3 -3
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/_instance.py +108 -51
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/_instance_bootstrap.py +35 -37
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/_poll.py +147 -14
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/allocator.py +9 -37
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/base.py +61 -50
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/lambdalabs/__init__.py +18 -5
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/lambdalabs/jobs/__init__.py +137 -30
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/lambdalabs/jobs/builders.py +4 -3
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/lambdalabs/pricing.py +1 -0
- freesolo_flash_dev-0.2.27/flash/providers/preflight.py +92 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/realized.py +5 -5
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/runpod/__init__.py +8 -2
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/runpod/api.py +100 -8
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/runpod/gpus.py +0 -5
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/runpod/jobs.py +180 -72
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/runpod/keys.py +15 -9
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/runpod/preload.py +42 -140
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/runpod/train/__init__.py +1 -50
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/runpod/train/deps.py +119 -53
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/runpod/train/endpoints.py +39 -29
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/runner/__init__.py +148 -23
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/runner/lifecycle.py +50 -40
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/schema/__init__.py +3 -3
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/serve/deploy.py +6 -0
- freesolo_flash_dev-0.2.27/flash/serve/export.py +176 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/serve/pricing.py +3 -0
- freesolo_flash_dev-0.2.27/flash/server/_deps.py +115 -0
- freesolo_flash_dev-0.2.27/flash/server/_locks.py +54 -0
- freesolo_flash_dev-0.2.27/flash/server/_runtime.py +238 -0
- freesolo_flash_dev-0.2.27/flash/server/app.py +371 -0
- freesolo_flash_dev-0.2.27/flash/server/billing_retry.py +122 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/server/checkpoints.py +35 -10
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/server/envs.py +3 -13
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/server/reconcile.py +1 -1
- freesolo_flash_dev-0.2.27/flash/server/routes/__init__.py +6 -0
- freesolo_flash_dev-0.2.27/flash/server/routes/envs.py +48 -0
- freesolo_flash_dev-0.2.27/flash/server/routes/meta.py +42 -0
- freesolo_flash_dev-0.2.27/flash/server/routes/runs.py +175 -0
- freesolo_flash_dev-0.2.27/flash/server/routes/serving.py +427 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/spec.py +4 -3
- freesolo_flash_dev-0.2.27/infisical-entrypoint.sh +45 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/pyproject.toml +9 -8
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/conftest.py +5 -6
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/live/conftest.py +2 -2
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_agent_flash_cli_contract.py +2 -2
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_allocator.py +37 -18
- freesolo_flash_dev-0.2.27/tests/test_billing_retry.py +803 -0
- freesolo_flash_dev-0.2.27/tests/test_checkpoints.py +591 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_cli_commands.py +59 -19
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_cli_errors.py +1 -1
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_cli_estimate.py +1 -1
- freesolo_flash_dev-0.2.27/tests/test_cli_help.py +96 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_cli_managed.py +1 -1
- freesolo_flash_dev-0.2.27/tests/test_cli_render_theme.py +312 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_client.py +44 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_client_server_integration.py +25 -1
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_config_overrides.py +1 -1
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_cost_analytical.py +23 -5
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_cost_equation.py +1 -1
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_cost_hardware.py +17 -12
- freesolo_flash_dev-0.2.27/tests/test_cost_models.py +64 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_dev_channel.py +11 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_disk_gb.py +9 -2
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_env_install.py +2 -2
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_env_push.py +2 -2
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_envs_coverage.py +2 -2
- freesolo_flash_dev-0.2.27/tests/test_export.py +466 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_flash_mvp.py +1 -27
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_flash_worker.py +152 -17
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_gpus.py +9 -8
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_grpo_params.py +249 -0
- freesolo_flash_dev-0.2.27/tests/test_idle_endpoint_reaper.py +478 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_jobs.py +284 -26
- freesolo_flash_dev-0.2.27/tests/test_kernel_fingerprint.py +132 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_lambda_runner.py +344 -19
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_login_perms.py +2 -2
- freesolo_flash_dev-0.2.27/tests/test_mig_guard.py +284 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_packing.py +352 -0
- freesolo_flash_dev-0.2.27/tests/test_poll_helpers.py +95 -0
- freesolo_flash_dev-0.2.27/tests/test_preflight.py +173 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_provider_routing.py +99 -32
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_provider_teardown_robustness.py +16 -63
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_providers_symmetry.py +38 -8
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_realized_cost.py +1 -1
- freesolo_flash_dev-0.2.27/tests/test_resume_on_retry.py +389 -0
- freesolo_flash_dev-0.2.27/tests/test_runpod_key_fingerprint.py +75 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_runpod_key_waterfall.py +29 -1
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_serve.py +49 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_server_api.py +289 -1
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_server_billing.py +23 -1
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_server_db.py +20 -1
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_thinking_config.py +1 -1
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_verifiers.py +81 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_version.py +1 -1
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_vl_warmstart_adapter_keys.py +38 -0
- freesolo_flash_dev-0.2.27/tests/test_vl_warmstart_recombine.py +351 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_vl_weight_sync.py +27 -3
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_weight_cache.py +264 -198
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_worker_dryrun.py +35 -24
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_worker_image.py +20 -1
- freesolo_flash_dev-0.2.27/tests/test_worker_init_heartbeat.py +357 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_worker_stack.py +202 -10
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/uv.lock +1 -1
- freesolo_flash_dev-0.2.25/.github/workflows/bake-kernel-cache.yml +0 -136
- freesolo_flash_dev-0.2.25/Dockerfile +0 -24
- freesolo_flash_dev-0.2.25/flash/cli/__init__.py +0 -1
- freesolo_flash_dev-0.2.25/flash/engine/worker/__init__.py +0 -2916
- freesolo_flash_dev-0.2.25/flash/engine/worker/perf.py +0 -1048
- freesolo_flash_dev-0.2.25/flash/envs/adapter/rubric.py +0 -222
- freesolo_flash_dev-0.2.25/flash/mcp/__init__.py +0 -1
- freesolo_flash_dev-0.2.25/flash/mcp/server.py +0 -85
- freesolo_flash_dev-0.2.25/flash/providers/hyperstack/__init__.py +0 -127
- freesolo_flash_dev-0.2.25/flash/providers/hyperstack/api.py +0 -522
- freesolo_flash_dev-0.2.25/flash/providers/hyperstack/auth.py +0 -17
- freesolo_flash_dev-0.2.25/flash/providers/hyperstack/gpus.py +0 -29
- freesolo_flash_dev-0.2.25/flash/providers/hyperstack/jobs/__init__.py +0 -632
- freesolo_flash_dev-0.2.25/flash/providers/hyperstack/jobs/builders.py +0 -122
- freesolo_flash_dev-0.2.25/flash/providers/hyperstack/preflight.py +0 -23
- freesolo_flash_dev-0.2.25/flash/providers/hyperstack/pricing.py +0 -26
- freesolo_flash_dev-0.2.25/flash/providers/hyperstack/train.py +0 -25
- freesolo_flash_dev-0.2.25/flash/providers/preflight.py +0 -55
- freesolo_flash_dev-0.2.25/flash/server/app.py +0 -961
- freesolo_flash_dev-0.2.25/tests/live/test_hyperstack_live.py +0 -50
- freesolo_flash_dev-0.2.25/tests/test_checkpoints.py +0 -298
- freesolo_flash_dev-0.2.25/tests/test_cli_render_theme.py +0 -159
- freesolo_flash_dev-0.2.25/tests/test_cost_models.py +0 -36
- freesolo_flash_dev-0.2.25/tests/test_hyperstack_runner.py +0 -1031
- freesolo_flash_dev-0.2.25/tests/test_idle_endpoint_reaper.py +0 -285
- freesolo_flash_dev-0.2.25/tests/test_mig_guard.py +0 -70
- freesolo_flash_dev-0.2.25/tests/test_poll_helpers.py +0 -52
- freesolo_flash_dev-0.2.25/tests/test_preflight.py +0 -98
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/.dockerignore +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/.github/workflows/ci.yml +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/.github/workflows/main-source-guard.yml +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/.github/workflows/publish-image.yml +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/.github/workflows/publish.yml +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/.gitignore +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/LICENSE +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/build/kernel_cache/.gitignore +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/build/kernel_cache/.keep +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/docker/make_rp_handler.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/docs/cli-style/index.html +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/docs/cli-style/preview.png +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/docs/kernel-cache.md +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/_fileio.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/_logging.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/_update_check.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/client/specs.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/cost/__init__.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/cost/types.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/engine/__init__.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/engine/multiturn_rollout.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/engine/worker/__main__.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/envs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/envs/base.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/envs/registry.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/lambdalabs/api.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/lambdalabs/auth.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/lambdalabs/gpus.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/lambdalabs/preflight.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/lambdalabs/train.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/runpod/auth.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/runpod/cost.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/runpod/preflight.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/runpod/pricing.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/providers/runpod/slots.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/py.typed +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/runner/checkpoints.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/runner/deploy.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/schema/fields.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/serve/__init__.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/server/__init__.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/server/__main__.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/server/auth.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/server/billing.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/server/db.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/server/environment_registry.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/flash/server/run_registry.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/scripts/build_dev_dist.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/__init__.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/_helpers/__init__.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/_helpers/runner.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/_helpers/specs.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/fixtures/math_eval.jsonl +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/fixtures/math_train.jsonl +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/live/__init__.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/live/test_lambda_live.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/live/test_runpod_live.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_algorithms.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_backend_jobspec_contract.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_cancel_remote.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_catalog_consistency.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_chalk_kernels.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_cost_estimate.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_cost_rewards.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_endpoint_name.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_env_publish.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_env_rate_limit_resolve.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_github_urlopen_retry.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_grpo_mask_aware.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_grpo_sleep_gate.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_kernel_cache.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_kv_util.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_logging.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_managed_hf_repo.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_metrics_schema_agent_contract.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_multiturn_rollout.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_open_model_policy.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_orchestrator_flash.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_resolve_params_b.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_runmgmt.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_runpod_api_delete.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_runpod_slots.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_serving_contract.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_spec_and_validation.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_update_check.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_wandb_naming.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_warmstart_cross_repo.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_worker_hardexit.py +0 -0
- {freesolo_flash_dev-0.2.25 → freesolo_flash_dev-0.2.27}/tests/test_worker_thinking.py +0 -0
|
@@ -5,6 +5,11 @@
|
|
|
5
5
|
# GPU substrate. RunPod is the default; Vast is opt-in (only required when set).
|
|
6
6
|
RUNPOD_API_KEY=
|
|
7
7
|
VAST_API_KEY=
|
|
8
|
+
# Use the per-arch baked worker images (cu128-<sm>) to skip the ~10-15 min cold-start JIT. Requires
|
|
9
|
+
# the per-SM images published first (.github/workflows/bake-kernel-cache.yml), and a manual re-bake
|
|
10
|
+
# after any worker-deps change. The control-plane Dockerfile sets this to 1 by default; uncomment to
|
|
11
|
+
# enable for a bare `flash-server` deploy.
|
|
12
|
+
# FLASH_WORKER_IMAGE_PER_SM=1
|
|
8
13
|
# HuggingFace token with write access to each run's [train] hf_repo (code upload +
|
|
9
14
|
# streamed checkpoints/adapters land in that per-run dataset repo). The artifact repo
|
|
10
15
|
# is per-run (set in the run TOML's [train] hf_repo), not an operator-wide env var.
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
name: auto-rebake per-arch kernel cache
|
|
2
|
+
|
|
3
|
+
# Keeps the per-arch ghcr.io/freesolo-co/flash-worker:cu128-sm* images fresh whenever the :cu128 base
|
|
4
|
+
# is rebuilt. Each cu128-smXX is physically `FROM :cu128` + a baked mega-cache, so a new base leaves
|
|
5
|
+
# the per-arch tags pointing at the OLD base until rebaked. "Out of date" has two flavors, keyed off
|
|
6
|
+
# the fp_cache / fp_base labels worker-image.yml stamps on :cu128 (docker/kernel_fingerprint.py):
|
|
7
|
+
#
|
|
8
|
+
# * fp_cache changed -> the baked Triton/Inductor mega-cache is INVALID -> a real GPU re-warm is
|
|
9
|
+
# required (bake-kernel-cache.yml). PAID, and AUTO-fired by this detection (no manual approval) --
|
|
10
|
+
# fp_cache only moves on a deliberate toolchain bump, so spend stays tied to real need.
|
|
11
|
+
# * fp_cache same but fp_base changed -> the cache is still valid, the base just moved (FA wheel /
|
|
12
|
+
# causal-conv1d / rp_handler / a non-kernel dep) -> a FREE re-layer grafts the existing cache onto
|
|
13
|
+
# the fresh base (docker/Dockerfile.kernelcache.relayer), no GPU.
|
|
14
|
+
# * both same -> skip (this is the pure-flash-code case the old reminder over-fired on).
|
|
15
|
+
#
|
|
16
|
+
# The gate READS the published labels (authoritative for what was actually built, incl. a manual FA3
|
|
17
|
+
# override) rather than recomputing from the checkout, and aborts unless the base's revision label ==
|
|
18
|
+
# the build's head_sha (so a superseded base build or a `tag=...` dispatch that never touched :cu128
|
|
19
|
+
# can't trigger a stale rebake).
|
|
20
|
+
|
|
21
|
+
on:
|
|
22
|
+
workflow_run:
|
|
23
|
+
workflows: ["flash worker image"]
|
|
24
|
+
types: [completed]
|
|
25
|
+
|
|
26
|
+
permissions:
|
|
27
|
+
contents: read
|
|
28
|
+
packages: write
|
|
29
|
+
actions: read
|
|
30
|
+
issues: write
|
|
31
|
+
|
|
32
|
+
# Scope the group to the triggering base build's commit so a no-op completion (e.g. a tag=cu128-test
|
|
33
|
+
# worker-image run, whose head_sha differs) can't cancel an in-flight real bake. The revision guard
|
|
34
|
+
# already neutralizes superseded runs, so a global cancel-in-progress isn't needed.
|
|
35
|
+
concurrency:
|
|
36
|
+
group: auto-rebake-${{ github.event.workflow_run.head_sha }}
|
|
37
|
+
cancel-in-progress: true
|
|
38
|
+
|
|
39
|
+
env:
|
|
40
|
+
BASE: ghcr.io/freesolo-co/flash-worker:cu128
|
|
41
|
+
|
|
42
|
+
jobs:
|
|
43
|
+
gate:
|
|
44
|
+
if: github.event.workflow_run.conclusion == 'success'
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
outputs:
|
|
47
|
+
proceed: ${{ steps.base.outputs.proceed }}
|
|
48
|
+
relayer_sms: ${{ steps.classify.outputs.relayer_sms }}
|
|
49
|
+
gpu_sms: ${{ steps.classify.outputs.gpu_sms }}
|
|
50
|
+
fp_cache: ${{ steps.base.outputs.fp_cache }}
|
|
51
|
+
fp_base: ${{ steps.base.outputs.fp_base }}
|
|
52
|
+
base_ref: ${{ steps.base.outputs.base_ref }}
|
|
53
|
+
base_revision: ${{ steps.base.outputs.revision }}
|
|
54
|
+
steps:
|
|
55
|
+
- uses: actions/checkout@v6
|
|
56
|
+
with:
|
|
57
|
+
ref: ${{ github.event.workflow_run.head_sha }}
|
|
58
|
+
|
|
59
|
+
- name: Install crane
|
|
60
|
+
run: |
|
|
61
|
+
curl -sSL https://github.com/google/go-containerregistry/releases/download/v0.20.2/go-containerregistry_Linux_x86_64.tar.gz \
|
|
62
|
+
| sudo tar -xz -C /usr/local/bin crane
|
|
63
|
+
crane version
|
|
64
|
+
|
|
65
|
+
- name: Log in to GHCR
|
|
66
|
+
uses: docker/login-action@v3
|
|
67
|
+
with:
|
|
68
|
+
registry: ghcr.io
|
|
69
|
+
username: ${{ github.actor }}
|
|
70
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
71
|
+
|
|
72
|
+
- name: Read base labels
|
|
73
|
+
id: base
|
|
74
|
+
env:
|
|
75
|
+
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
|
|
76
|
+
run: |
|
|
77
|
+
# canonical digest ref (repo@sha256, no tag) so every tool (crane/docker/runpod) accepts it.
|
|
78
|
+
digest=$(crane digest "$BASE")
|
|
79
|
+
repo=${BASE%:*}
|
|
80
|
+
cfg=$(crane config "$repo@$digest")
|
|
81
|
+
fpc=$(echo "$cfg" | jq -r '.config.Labels["co.freesolo.flash.kernelcache.fp_cache"] // ""')
|
|
82
|
+
fpb=$(echo "$cfg" | jq -r '.config.Labels["co.freesolo.flash.kernelcache.fp_base"] // ""')
|
|
83
|
+
rev=$(echo "$cfg" | jq -r '.config.Labels["org.opencontainers.image.revision"] // ""')
|
|
84
|
+
echo "base_ref=$repo@$digest" >> "$GITHUB_OUTPUT"
|
|
85
|
+
echo "fp_cache=$fpc" >> "$GITHUB_OUTPUT"
|
|
86
|
+
echo "fp_base=$fpb" >> "$GITHUB_OUTPUT"
|
|
87
|
+
echo "revision=$rev" >> "$GITHUB_OUTPUT"
|
|
88
|
+
# Only proceed if this base IS the build that triggered us: its revision label must match the
|
|
89
|
+
# build's head_sha. Guards (a) a newer base that already superseded this run, and (b) a
|
|
90
|
+
# `tag=cu128-test` dispatch that never updated :cu128 (the workflow_run payload can't reveal
|
|
91
|
+
# the dispatch tag). An unlabeled base (pre-rollout) also no-ops here.
|
|
92
|
+
if [ -n "$fpc" ] && [ "$rev" = "$HEAD_SHA" ]; then
|
|
93
|
+
echo "proceed=true" >> "$GITHUB_OUTPUT"
|
|
94
|
+
echo "base $BASE@$digest is rev $rev (== head_sha) fp_cache=$fpc fp_base=$fpb"
|
|
95
|
+
else
|
|
96
|
+
echo "proceed=false" >> "$GITHUB_OUTPUT"
|
|
97
|
+
echo "::notice::auto-rebake no-op: base rev '$rev' != head_sha '$HEAD_SHA' (or base unlabeled)"
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
- name: Classify arches
|
|
101
|
+
id: classify
|
|
102
|
+
if: steps.base.outputs.proceed == 'true'
|
|
103
|
+
env:
|
|
104
|
+
FP_CACHE: ${{ steps.base.outputs.fp_cache }}
|
|
105
|
+
FP_BASE: ${{ steps.base.outputs.fp_base }}
|
|
106
|
+
REV: ${{ steps.base.outputs.revision }}
|
|
107
|
+
run: |
|
|
108
|
+
# arch list is the single source of truth in deps.py (mirrored by the bake matrix; a test
|
|
109
|
+
# pins that mirror). textual parse -> no flash import / uv sync needed.
|
|
110
|
+
arches=$(python3 -c "import re,pathlib; t=pathlib.Path('flash/providers/runpod/train/deps.py').read_text(); m=re.search(r'BAKED_PER_SM_ARCHES\s*=\s*frozenset\(\{([^}]+)\}\)', t); print(' '.join(re.findall(r'sm\d+', m.group(1))))")
|
|
111
|
+
relayer=(); gpu=()
|
|
112
|
+
summary="| arch | published fp_cache | published fp_base | action |\n|---|---|---|---|\n"
|
|
113
|
+
for sm in $arches; do
|
|
114
|
+
img="ghcr.io/freesolo-co/flash-worker:cu128-$sm"
|
|
115
|
+
if cfg=$(crane config "$img" 2>/dev/null); then
|
|
116
|
+
pc=$(echo "$cfg" | jq -r '.config.Labels["co.freesolo.flash.kernelcache.fp_cache"] // ""')
|
|
117
|
+
pb=$(echo "$cfg" | jq -r '.config.Labels["co.freesolo.flash.kernelcache.fp_base"] // ""')
|
|
118
|
+
else
|
|
119
|
+
pc="(missing)"; pb="(missing)"
|
|
120
|
+
fi
|
|
121
|
+
if [ "$pc" != "$FP_CACHE" ]; then
|
|
122
|
+
gpu+=("$sm"); action="GPU re-warm (cache invalid)"
|
|
123
|
+
elif [ "$pb" != "$FP_BASE" ]; then
|
|
124
|
+
relayer+=("$sm"); action="re-layer (free)"
|
|
125
|
+
else
|
|
126
|
+
action="up to date"
|
|
127
|
+
fi
|
|
128
|
+
echo "$sm: pc=$pc pb=$pb -> $action"
|
|
129
|
+
summary="$summary| $sm | \`$pc\` | \`$pb\` | $action |\n"
|
|
130
|
+
done
|
|
131
|
+
if [ ${#relayer[@]} -gt 0 ]; then
|
|
132
|
+
relayer_json=$(printf '%s\n' "${relayer[@]}" | jq -R . | jq -cs .)
|
|
133
|
+
else
|
|
134
|
+
relayer_json='[]'
|
|
135
|
+
fi
|
|
136
|
+
gpu_csv=$(IFS=,; echo "${gpu[*]}")
|
|
137
|
+
echo "relayer_sms=$relayer_json" >> "$GITHUB_OUTPUT"
|
|
138
|
+
echo "gpu_sms=$gpu_csv" >> "$GITHUB_OUTPUT"
|
|
139
|
+
{
|
|
140
|
+
echo "### auto-rebake plan (base rev \`$REV\`, fp_cache \`$FP_CACHE\` fp_base \`$FP_BASE\`)"
|
|
141
|
+
echo ""
|
|
142
|
+
echo -e "$summary"
|
|
143
|
+
echo ""
|
|
144
|
+
echo "re-layer (free): \`$relayer_json\` | GPU re-warm (paid, auto-started): \`${gpu_csv:-none}\`"
|
|
145
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
146
|
+
|
|
147
|
+
# FYI notification (not an action item -- the bake auto-runs). Keeps a heads-up + cost trail of
|
|
148
|
+
# which arches a paid GPU re-warm fired for.
|
|
149
|
+
- name: Notify GPU re-bake auto-started
|
|
150
|
+
if: steps.base.outputs.proceed == 'true' && steps.classify.outputs.gpu_sms != ''
|
|
151
|
+
env:
|
|
152
|
+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
153
|
+
GPU_SMS: ${{ steps.classify.outputs.gpu_sms }}
|
|
154
|
+
REV: ${{ steps.base.outputs.revision }}
|
|
155
|
+
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
156
|
+
run: |
|
|
157
|
+
title="kernel-cache: per-arch GPU re-bake auto-started"
|
|
158
|
+
body=$(printf 'The kernel toolchain changed (fp_cache), so a PAID GPU re-warm auto-started for these per-arch images (no approval needed):\n\n- arches: `%s`\n- base rev: `%s`\n\nTrack it in the run: %s' "$GPU_SMS" "$REV" "$RUN_URL")
|
|
159
|
+
# best-effort: a labeling/permission hiccup must never fail the gate. self-heal the label so
|
|
160
|
+
# no manual setup is needed, then reuse one open issue (comment) instead of spamming new ones.
|
|
161
|
+
gh label create kernel-rebake-pending --color B60205 --description "per-arch kernel cache GPU re-bake auto-started" 2>/dev/null || true
|
|
162
|
+
existing=$(gh issue list --label kernel-rebake-pending --state open --json number -q '.[0].number' 2>/dev/null || true)
|
|
163
|
+
if [ -n "${existing:-}" ]; then
|
|
164
|
+
gh issue comment "$existing" --body "$body" || true
|
|
165
|
+
else
|
|
166
|
+
gh issue create --title "$title" --body "$body" --label kernel-rebake-pending \
|
|
167
|
+
|| gh issue create --title "$title" --body "$body" || true
|
|
168
|
+
fi
|
|
169
|
+
|
|
170
|
+
# FREE path: graft the existing valid cache onto the fresh base, no GPU. Runs automatically.
|
|
171
|
+
relayer:
|
|
172
|
+
needs: gate
|
|
173
|
+
if: needs.gate.outputs.proceed == 'true' && needs.gate.outputs.relayer_sms != '[]' && needs.gate.outputs.relayer_sms != ''
|
|
174
|
+
runs-on: ubuntu-24.04-8core
|
|
175
|
+
strategy:
|
|
176
|
+
fail-fast: false
|
|
177
|
+
matrix:
|
|
178
|
+
sm: ${{ fromJson(needs.gate.outputs.relayer_sms || '[]') }}
|
|
179
|
+
env:
|
|
180
|
+
BASE_REF: ${{ needs.gate.outputs.base_ref }}
|
|
181
|
+
FP_CACHE: ${{ needs.gate.outputs.fp_cache }}
|
|
182
|
+
FP_BASE: ${{ needs.gate.outputs.fp_base }}
|
|
183
|
+
BASE_REVISION: ${{ needs.gate.outputs.base_revision }}
|
|
184
|
+
steps:
|
|
185
|
+
- uses: actions/checkout@v6
|
|
186
|
+
with:
|
|
187
|
+
ref: ${{ github.event.workflow_run.head_sha }}
|
|
188
|
+
|
|
189
|
+
# The graft pulls two ~20GB images (old per-arch tag + fresh base); reclaim runner disk first.
|
|
190
|
+
- name: Free disk space
|
|
191
|
+
run: |
|
|
192
|
+
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/.ghcup \
|
|
193
|
+
/usr/local/lib/android /opt/hostedtoolcache /usr/share/swift \
|
|
194
|
+
/usr/local/share/boost /usr/local/lib/node_modules \
|
|
195
|
+
/usr/local/share/powershell /usr/share/miniconda || true
|
|
196
|
+
sudo apt-get clean || true
|
|
197
|
+
sudo docker image prune -af || true
|
|
198
|
+
df -h /
|
|
199
|
+
|
|
200
|
+
- uses: docker/setup-buildx-action@v3
|
|
201
|
+
|
|
202
|
+
- name: Log in to GHCR
|
|
203
|
+
uses: docker/login-action@v3
|
|
204
|
+
with:
|
|
205
|
+
registry: ghcr.io
|
|
206
|
+
username: ${{ github.actor }}
|
|
207
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
208
|
+
|
|
209
|
+
# Pin OLDCACHE by digest so a concurrent overwrite of the source tag can't graft a wrong cache.
|
|
210
|
+
- name: Resolve old per-arch cache digest
|
|
211
|
+
id: old
|
|
212
|
+
run: |
|
|
213
|
+
img="ghcr.io/freesolo-co/flash-worker:cu128-${{ matrix.sm }}"
|
|
214
|
+
digest=$(docker buildx imagetools inspect "$img" --format '{{.Manifest.Digest}}')
|
|
215
|
+
# canonical digest ref (repo@sha256, no tag) for the FROM in the relayer build.
|
|
216
|
+
echo "ref=${img%:*}@$digest" >> "$GITHUB_OUTPUT"
|
|
217
|
+
|
|
218
|
+
- name: Re-layer cache onto the fresh base (no GPU)
|
|
219
|
+
uses: docker/build-push-action@v6
|
|
220
|
+
with:
|
|
221
|
+
context: .
|
|
222
|
+
file: docker/Dockerfile.kernelcache.relayer
|
|
223
|
+
push: true
|
|
224
|
+
tags: ghcr.io/freesolo-co/flash-worker:cu128-${{ matrix.sm }}
|
|
225
|
+
provenance: false
|
|
226
|
+
build-args: |
|
|
227
|
+
BASE=${{ env.BASE_REF }}
|
|
228
|
+
OLDCACHE=${{ steps.old.outputs.ref }}
|
|
229
|
+
FP_CACHE=${{ env.FP_CACHE }}
|
|
230
|
+
FP_BASE=${{ env.FP_BASE }}
|
|
231
|
+
BASE_REVISION=${{ env.BASE_REVISION }}
|
|
232
|
+
|
|
233
|
+
# PAID path: re-warm the cache on a real GPU per arch. Runs automatically -- the fingerprint gate
|
|
234
|
+
# above is the only thing that decides it fires (only when fp_cache changed), so no manual approval.
|
|
235
|
+
gpu-warm:
|
|
236
|
+
needs: gate
|
|
237
|
+
if: needs.gate.outputs.proceed == 'true' && needs.gate.outputs.gpu_sms != ''
|
|
238
|
+
uses: ./.github/workflows/bake-kernel-cache.yml
|
|
239
|
+
with:
|
|
240
|
+
sms: ${{ needs.gate.outputs.gpu_sms }}
|
|
241
|
+
fp_cache: ${{ needs.gate.outputs.fp_cache }}
|
|
242
|
+
fp_base: ${{ needs.gate.outputs.fp_base }}
|
|
243
|
+
base_ref: ${{ needs.gate.outputs.base_ref }}
|
|
244
|
+
base_revision: ${{ needs.gate.outputs.base_revision }}
|
|
245
|
+
secrets: inherit
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
name: flash worker kernel-cache (per-arch)
|
|
2
|
+
|
|
3
|
+
# Builds per-SM worker images with the compiled-kernel mega-cache baked in, killing the ~10-15 min
|
|
4
|
+
# first-use JIT on a cold worker (the regression #194 reintroduced). Each matrix leg:
|
|
5
|
+
# 1. offloads the warmup to a RunPod GPU of that arch (GH runners have none) -> build/kernel_cache,
|
|
6
|
+
# 2. docker build --build-arg BUILD_KERNEL_CACHE=true -> ghcr.io/freesolo-co/flash-worker:cu128-<sm>,
|
|
7
|
+
# 3. pushes it. Activate by setting FLASH_WORKER_IMAGE_PER_SM=1 on the control plane so the
|
|
8
|
+
# allocator selects the cu128-<sm> tag per GPU class (see flash/providers/runpod/train/deps.py).
|
|
9
|
+
#
|
|
10
|
+
# Depends on the base ghcr.io/freesolo-co/flash-worker:cu128 image existing (worker-image.yml) — the
|
|
11
|
+
# warmup runs INSIDE it so the cache matches the image's pinned torch/triton/fla/liger toolchain.
|
|
12
|
+
#
|
|
13
|
+
# Secrets: RUNPOD_API_KEY (the warmup GPU pod) + HF_TOKEN (the temp dataset that ferries code + the
|
|
14
|
+
# cache artifact) are pulled from Infisical (/flash, prod) via the bootstrap INFISICAL_CLIENT_ID /
|
|
15
|
+
# CLIENT_SECRET / PROJECT_ID repo secrets; the GHCR push uses the default GITHUB_TOKEN. Heavy + paid,
|
|
16
|
+
# but UNGATED: the fingerprint detection in auto-rebake.yml decides WHEN this runs (only on a real
|
|
17
|
+
# kernel-toolchain change), so there is no manual approval step. Invoked by hand (workflow_dispatch)
|
|
18
|
+
# OR automatically by auto-rebake.yml (workflow_call) when the kernel toolchain changed.
|
|
19
|
+
|
|
20
|
+
on:
|
|
21
|
+
workflow_dispatch:
|
|
22
|
+
inputs:
|
|
23
|
+
sms:
|
|
24
|
+
description: "comma-separated sm list to bake; default is all validated arches"
|
|
25
|
+
default: "sm80,sm86,sm89,sm90,sm120"
|
|
26
|
+
required: false
|
|
27
|
+
# auto-rebake.yml calls this for the EXPENSIVE path (the cache is invalid and must be re-warmed on a
|
|
28
|
+
# real GPU). It passes the arch subset, the fingerprints to stamp, and a digest-pinned base so the
|
|
29
|
+
# warm runs inside the exact :cu128 the per-arch tag will sit on.
|
|
30
|
+
workflow_call:
|
|
31
|
+
inputs:
|
|
32
|
+
sms:
|
|
33
|
+
type: string
|
|
34
|
+
required: true
|
|
35
|
+
fp_cache:
|
|
36
|
+
type: string
|
|
37
|
+
required: false
|
|
38
|
+
default: ""
|
|
39
|
+
fp_base:
|
|
40
|
+
type: string
|
|
41
|
+
required: false
|
|
42
|
+
default: ""
|
|
43
|
+
base_ref:
|
|
44
|
+
type: string
|
|
45
|
+
required: false
|
|
46
|
+
default: "ghcr.io/freesolo-co/flash-worker:cu128"
|
|
47
|
+
base_revision:
|
|
48
|
+
type: string
|
|
49
|
+
required: false
|
|
50
|
+
default: ""
|
|
51
|
+
|
|
52
|
+
permissions:
|
|
53
|
+
contents: read
|
|
54
|
+
packages: write
|
|
55
|
+
|
|
56
|
+
jobs:
|
|
57
|
+
# Ungated: the PAID RunPod bake runs as soon as it's called/dispatched. The decision of WHEN to run
|
|
58
|
+
# lives in auto-rebake.yml's fingerprint gate (it only invokes this when fp_cache changed), so a
|
|
59
|
+
# second manual approval here would be redundant. Re-add an `approve` job with `environment: gpu-bake`
|
|
60
|
+
# (and `needs: approve`) if you ever want a human checkpoint back.
|
|
61
|
+
bake:
|
|
62
|
+
runs-on: ubuntu-24.04-8core
|
|
63
|
+
strategy:
|
|
64
|
+
fail-fast: false
|
|
65
|
+
matrix:
|
|
66
|
+
include:
|
|
67
|
+
- {
|
|
68
|
+
sm: sm80,
|
|
69
|
+
arch: "8.0",
|
|
70
|
+
gpu_type_id: "NVIDIA A100 80GB PCIe",
|
|
71
|
+
allowed_cuda: "",
|
|
72
|
+
}
|
|
73
|
+
- {
|
|
74
|
+
sm: sm86,
|
|
75
|
+
arch: "8.6",
|
|
76
|
+
gpu_type_id: "NVIDIA RTX A6000",
|
|
77
|
+
allowed_cuda: "",
|
|
78
|
+
}
|
|
79
|
+
- {
|
|
80
|
+
sm: sm89,
|
|
81
|
+
arch: "8.9",
|
|
82
|
+
gpu_type_id: "NVIDIA GeForce RTX 4090",
|
|
83
|
+
allowed_cuda: "",
|
|
84
|
+
}
|
|
85
|
+
- {
|
|
86
|
+
sm: sm90,
|
|
87
|
+
arch: "9.0",
|
|
88
|
+
gpu_type_id: "NVIDIA H100 80GB HBM3",
|
|
89
|
+
allowed_cuda: "",
|
|
90
|
+
}
|
|
91
|
+
# Blackwell needs CUDA-13 hosts to JIT its PTX (matches min_cuda_for in the provider). Bake
|
|
92
|
+
# sm120 on the RTX Pro 6000 (Server Edition): it's the same sm120 (cache is sm-keyed, so it's
|
|
93
|
+
# valid for RTX 5090 too) but has far better secure-cloud on-demand capacity -- the RTX 5090
|
|
94
|
+
# pool repeatedly returned "machine does not have the resources" at create_pod.
|
|
95
|
+
- {
|
|
96
|
+
sm: sm120,
|
|
97
|
+
arch: "12.0",
|
|
98
|
+
gpu_type_id: "NVIDIA RTX PRO 6000 Blackwell Server Edition",
|
|
99
|
+
allowed_cuda: "13.0",
|
|
100
|
+
}
|
|
101
|
+
# Datacenter Blackwell (sm100, distinct from the sm120 RTX Pro 6000). B200 has 180 GB and
|
|
102
|
+
# good secure-cloud capacity. CUDA-13 host like the other Blackwell parts. NOT in the default
|
|
103
|
+
# `sms` list yet (B200 is unvalidated) -- bake it explicitly via `sms=sm100` until a smoke
|
|
104
|
+
# passes, then add it to the default.
|
|
105
|
+
- {
|
|
106
|
+
sm: sm100,
|
|
107
|
+
arch: "10.0",
|
|
108
|
+
gpu_type_id: "NVIDIA B200",
|
|
109
|
+
allowed_cuda: "13.0",
|
|
110
|
+
}
|
|
111
|
+
steps:
|
|
112
|
+
# Gate FIRST (no checkout needed) so skipped arches do no crane / checkout / build work.
|
|
113
|
+
- name: Should bake ${{ matrix.sm }}?
|
|
114
|
+
id: gate
|
|
115
|
+
run: |
|
|
116
|
+
req="${{ inputs.sms || 'sm80,sm86,sm89,sm90,sm120' }}"
|
|
117
|
+
req="${req// /}" # tolerate spaces, e.g. "sm80, sm86"
|
|
118
|
+
case ",$req," in
|
|
119
|
+
*,${{ matrix.sm }},*) echo "run=true" >> "$GITHUB_OUTPUT" ;;
|
|
120
|
+
*) echo "run=false" >> "$GITHUB_OUTPUT"; echo "skipping ${{ matrix.sm }}" ;;
|
|
121
|
+
esac
|
|
122
|
+
|
|
123
|
+
- name: Install crane
|
|
124
|
+
if: steps.gate.outputs.run == 'true'
|
|
125
|
+
run: |
|
|
126
|
+
curl -sSL https://github.com/google/go-containerregistry/releases/download/v0.20.2/go-containerregistry_Linux_x86_64.tar.gz \
|
|
127
|
+
| sudo tar -xz -C /usr/local/bin crane
|
|
128
|
+
crane version
|
|
129
|
+
|
|
130
|
+
- name: Log in to GHCR
|
|
131
|
+
if: steps.gate.outputs.run == 'true'
|
|
132
|
+
uses: docker/login-action@v3
|
|
133
|
+
with:
|
|
134
|
+
registry: ghcr.io
|
|
135
|
+
username: ${{ github.actor }}
|
|
136
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
137
|
+
|
|
138
|
+
# Resolve the base + fingerprints BEFORE checkout, so the checkout (hence the warmed flash code)
|
|
139
|
+
# is pinned to the EXACT commit the base was built from. auto-rebake passes everything (fast path);
|
|
140
|
+
# a manual dispatch READS the labels off the published base, so the per-arch image always matches
|
|
141
|
+
# the base it extends (same source of truth as the gate) instead of recomputing from the checkout.
|
|
142
|
+
- name: Resolve base + fingerprints
|
|
143
|
+
id: base
|
|
144
|
+
if: steps.gate.outputs.run == 'true'
|
|
145
|
+
run: |
|
|
146
|
+
if [ -n "${{ inputs.fp_cache }}" ]; then
|
|
147
|
+
echo "ref=${{ inputs.base_ref }}" >> "$GITHUB_OUTPUT"
|
|
148
|
+
echo "fp_cache=${{ inputs.fp_cache }}" >> "$GITHUB_OUTPUT"
|
|
149
|
+
echo "fp_base=${{ inputs.fp_base }}" >> "$GITHUB_OUTPUT"
|
|
150
|
+
echo "revision=${{ inputs.base_revision }}" >> "$GITHUB_OUTPUT"
|
|
151
|
+
else
|
|
152
|
+
base="${{ inputs.base_ref || 'ghcr.io/freesolo-co/flash-worker:cu128' }}"
|
|
153
|
+
digest=$(crane digest "$base"); repo=${base%:*}
|
|
154
|
+
cfg=$(crane config "$repo@$digest")
|
|
155
|
+
echo "ref=$repo@$digest" >> "$GITHUB_OUTPUT"
|
|
156
|
+
echo "fp_cache=$(echo "$cfg" | jq -r '.config.Labels["co.freesolo.flash.kernelcache.fp_cache"] // ""')" >> "$GITHUB_OUTPUT"
|
|
157
|
+
echo "fp_base=$(echo "$cfg" | jq -r '.config.Labels["co.freesolo.flash.kernelcache.fp_base"] // ""')" >> "$GITHUB_OUTPUT"
|
|
158
|
+
echo "revision=$(echo "$cfg" | jq -r '.config.Labels["org.opencontainers.image.revision"] // ""')" >> "$GITHUB_OUTPUT"
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
# Pin the checkout to the base's source commit so the warmed flash code matches the base exactly
|
|
162
|
+
# (the cache is content-keyed by the code that compiled it). Falls back to the event sha if the
|
|
163
|
+
# base predates the revision label.
|
|
164
|
+
- uses: actions/checkout@v6
|
|
165
|
+
if: steps.gate.outputs.run == 'true'
|
|
166
|
+
with:
|
|
167
|
+
ref: ${{ steps.base.outputs.revision || github.sha }}
|
|
168
|
+
|
|
169
|
+
- name: Install uv
|
|
170
|
+
if: steps.gate.outputs.run == 'true'
|
|
171
|
+
uses: astral-sh/setup-uv@v5
|
|
172
|
+
with:
|
|
173
|
+
# no cache: the post-run cache-prune calls `uv`, but the "Free disk space" step deletes
|
|
174
|
+
# /opt/hostedtoolcache (where setup-uv put uv), so the post step fails ("uv not found") and
|
|
175
|
+
# marks the whole job red even though the bake + push already succeeded.
|
|
176
|
+
enable-cache: false
|
|
177
|
+
|
|
178
|
+
- name: Sync deps (flash + runpod + hf)
|
|
179
|
+
if: steps.gate.outputs.run == 'true'
|
|
180
|
+
run: uv sync --extra server
|
|
181
|
+
|
|
182
|
+
- name: Install Infisical CLI
|
|
183
|
+
if: steps.gate.outputs.run == 'true'
|
|
184
|
+
run: |
|
|
185
|
+
curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | sudo -E bash
|
|
186
|
+
sudo apt-get update && sudo apt-get install -y infisical
|
|
187
|
+
|
|
188
|
+
# RUNPOD_API_KEY + HF_TOKEN come from Infisical (/flash, prod) -- mirrors infisical-entrypoint.sh;
|
|
189
|
+
# the CLI takes the project UUID directly (the secrets-action wants a slug). `env -u
|
|
190
|
+
# FLASH_CHALK_SPEC` drops any chalk-spec override living in /flash so the bake always warms the
|
|
191
|
+
# repo-default chalk (the value the fingerprint hashed) -- otherwise the image could be warmed with
|
|
192
|
+
# one chalk and labeled with another. BASE_IMAGE is digest-pinned so the warm runs inside the EXACT
|
|
193
|
+
# base the per-arch tag will sit on.
|
|
194
|
+
- name: Warm kernels on a RunPod ${{ matrix.sm }} GPU -> build/kernel_cache
|
|
195
|
+
if: steps.gate.outputs.run == 'true'
|
|
196
|
+
env:
|
|
197
|
+
INFISICAL_CLIENT_ID: ${{ secrets.INFISICAL_CLIENT_ID }}
|
|
198
|
+
INFISICAL_CLIENT_SECRET: ${{ secrets.INFISICAL_CLIENT_SECRET }}
|
|
199
|
+
INFISICAL_PROJECT_ID: ${{ secrets.INFISICAL_PROJECT_ID }}
|
|
200
|
+
BASE_IMAGE: ${{ steps.base.outputs.ref }}
|
|
201
|
+
run: |
|
|
202
|
+
INFISICAL_TOKEN="$(infisical login --method=universal-auth \
|
|
203
|
+
--client-id="$INFISICAL_CLIENT_ID" --client-secret="$INFISICAL_CLIENT_SECRET" \
|
|
204
|
+
--silent --plain)"
|
|
205
|
+
export INFISICAL_TOKEN
|
|
206
|
+
infisical run --projectId "$INFISICAL_PROJECT_ID" --env prod --path /flash -- \
|
|
207
|
+
env -u FLASH_CHALK_SPEC \
|
|
208
|
+
uv run python docker/bake_kernel_cache.py \
|
|
209
|
+
--arch "${{ matrix.arch }}" --sm "${{ matrix.sm }}" \
|
|
210
|
+
--gpu-type-id "${{ matrix.gpu_type_id }}" \
|
|
211
|
+
--allowed-cuda "${{ matrix.allowed_cuda }}" \
|
|
212
|
+
--image "$BASE_IMAGE" \
|
|
213
|
+
--out build/kernel_cache
|
|
214
|
+
|
|
215
|
+
- name: Free disk space
|
|
216
|
+
if: steps.gate.outputs.run == 'true'
|
|
217
|
+
run: |
|
|
218
|
+
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/.ghcup \
|
|
219
|
+
/usr/local/lib/android /opt/hostedtoolcache /usr/share/swift \
|
|
220
|
+
/usr/local/share/boost /usr/local/lib/node_modules \
|
|
221
|
+
/usr/local/share/powershell /usr/share/miniconda || true
|
|
222
|
+
sudo apt-get clean || true
|
|
223
|
+
sudo docker image prune -af || true
|
|
224
|
+
df -h /
|
|
225
|
+
|
|
226
|
+
- uses: docker/setup-buildx-action@v3
|
|
227
|
+
if: steps.gate.outputs.run == 'true'
|
|
228
|
+
|
|
229
|
+
# thin layer FROM the SAME base the warmup ran inside -> the cache toolchain always matches the
|
|
230
|
+
# shipped image (rebuilding the full Dockerfile.worker from the checkout could drift from the
|
|
231
|
+
# published base and bake a cache that silently won't load). GHCR auth from the login step above.
|
|
232
|
+
- name: Build + push baked per-sm image (thin layer on the warmed base)
|
|
233
|
+
if: steps.gate.outputs.run == 'true'
|
|
234
|
+
uses: docker/build-push-action@v6
|
|
235
|
+
with:
|
|
236
|
+
context: .
|
|
237
|
+
file: docker/Dockerfile.kernelcache
|
|
238
|
+
push: true
|
|
239
|
+
tags: ghcr.io/freesolo-co/flash-worker:cu128-${{ matrix.sm }}
|
|
240
|
+
provenance: false
|
|
241
|
+
build-args: |
|
|
242
|
+
BASE=${{ steps.base.outputs.ref }}
|
|
243
|
+
FP_CACHE=${{ steps.base.outputs.fp_cache }}
|
|
244
|
+
FP_BASE=${{ steps.base.outputs.fp_base }}
|
|
245
|
+
BASE_REVISION=${{ steps.base.outputs.revision }}
|
|
@@ -8,7 +8,8 @@ name: Publish flash dev-channel package
|
|
|
8
8
|
# merging to `dev` cuts a release; ordinary dev pushes (version unchanged -> already published)
|
|
9
9
|
# no-op. This mirrors freesolo-flash's publish.yml, but keyed on the dev version and on `dev`
|
|
10
10
|
# instead of `main`, and a no-op is a clean success (not a failure) since most dev pushes don't
|
|
11
|
-
# bump it. Manual runs via workflow_dispatch
|
|
11
|
+
# bump it. Manual runs via workflow_dispatch re-run the same version check for the current dev
|
|
12
|
+
# version (still a no-op when it's already on PyPI — not a forced re-publish).
|
|
12
13
|
on:
|
|
13
14
|
push:
|
|
14
15
|
branches:
|
|
@@ -45,15 +46,28 @@ jobs:
|
|
|
45
46
|
with:
|
|
46
47
|
python-version: "3.11"
|
|
47
48
|
|
|
48
|
-
- name: Read dev-channel version
|
|
49
|
+
- name: Read dev-channel version (and enforce parity with the prod version)
|
|
49
50
|
id: meta
|
|
50
51
|
run: |
|
|
51
52
|
python3 - <<'PY' >> "$GITHUB_OUTPUT"
|
|
53
|
+
import sys
|
|
52
54
|
import tomllib
|
|
53
55
|
|
|
54
56
|
with open("pyproject.toml", "rb") as f:
|
|
55
57
|
data = tomllib.load(f)
|
|
56
|
-
|
|
58
|
+
dev_version = data["tool"]["flash-dev"]["version"]
|
|
59
|
+
prod_version = data["project"]["version"]
|
|
60
|
+
# The two channels MUST ship in lockstep (version-parity.yml enforces this on PRs). Re-check
|
|
61
|
+
# it here so the PUBLISH path can never ship a dev wheel out of sync with freesolo-flash even
|
|
62
|
+
# if the parity CI was bypassed or branch protection isn't strict — fail before build/publish.
|
|
63
|
+
if dev_version != prod_version:
|
|
64
|
+
print(
|
|
65
|
+
f"::error::version mismatch: [tool.flash-dev].version={dev_version} != "
|
|
66
|
+
f"[project].version={prod_version}; bump both in lockstep before publishing.",
|
|
67
|
+
file=sys.stderr,
|
|
68
|
+
)
|
|
69
|
+
sys.exit(1)
|
|
70
|
+
print(f"version={dev_version}")
|
|
57
71
|
PY
|
|
58
72
|
|
|
59
73
|
- name: Decide whether to publish
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: Version parity
|
|
2
|
+
|
|
3
|
+
# Keep the two release channels pinned to the same version: the prod package `freesolo-flash`
|
|
4
|
+
# (pyproject `[project].version`) and the dev-channel package `freesolo-flash-dev`
|
|
5
|
+
# (`[tool.flash-dev].version`). They publish from `main` and `dev` respectively, so a divergence
|
|
6
|
+
# would ship two channels claiming different versions. Bump both together.
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
branches: [main, dev]
|
|
10
|
+
pull_request:
|
|
11
|
+
branches: [main, dev]
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
versions-match:
|
|
18
|
+
name: dev and main at the same version
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v6
|
|
22
|
+
|
|
23
|
+
# tomllib is stdlib only since 3.11; the runner's default python3 may predate that.
|
|
24
|
+
- name: Set up Python
|
|
25
|
+
uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.11"
|
|
28
|
+
|
|
29
|
+
- name: Compare channel versions
|
|
30
|
+
run: |
|
|
31
|
+
python3 - <<'PY'
|
|
32
|
+
import sys
|
|
33
|
+
import tomllib
|
|
34
|
+
|
|
35
|
+
with open("pyproject.toml", "rb") as f:
|
|
36
|
+
data = tomllib.load(f)
|
|
37
|
+
|
|
38
|
+
prod = data["project"]["version"]
|
|
39
|
+
dev = data["tool"]["flash-dev"]["version"]
|
|
40
|
+
if prod != dev:
|
|
41
|
+
print(
|
|
42
|
+
"::error::Channel version mismatch: "
|
|
43
|
+
f"[project].version={prod} (freesolo-flash) != "
|
|
44
|
+
f"[tool.flash-dev].version={dev} (freesolo-flash-dev). "
|
|
45
|
+
"Bump both to the same version."
|
|
46
|
+
)
|
|
47
|
+
sys.exit(1)
|
|
48
|
+
print(f"OK: freesolo-flash and freesolo-flash-dev are both at {prod}.")
|
|
49
|
+
PY
|
|
@@ -36,17 +36,40 @@ on:
|
|
|
36
36
|
# so handler changes (e.g. the weight-cache preload branch) MUST rebuild the image. flash/**
|
|
37
37
|
# covers the handler source (endpoints.py); the generator lives outside flash/, so watch it too.
|
|
38
38
|
- docker/make_rp_handler.py
|
|
39
|
+
# The fp_cache/fp_base labels are computed by this script at build time, so a change to the
|
|
40
|
+
# fingerprint logic must rebuild :cu128 to re-stamp them (else auto-rebake never reclassifies).
|
|
41
|
+
- docker/kernel_fingerprint.py
|
|
39
42
|
|
|
40
43
|
permissions:
|
|
41
44
|
contents: read
|
|
42
45
|
packages: write
|
|
43
46
|
|
|
47
|
+
# A newer base build supersedes an older one: cancel the in-flight run so auto-rebake (which triggers
|
|
48
|
+
# on this workflow's completion) only ever reasons about the latest :cu128. The tag is in the group so
|
|
49
|
+
# a non-prod `tag=cu128-test` dispatch can't cancel an in-flight prod `cu128` push on the same ref
|
|
50
|
+
# (which would leave :cu128 unpublished for that commit and auto-rebake blind to it).
|
|
51
|
+
concurrency:
|
|
52
|
+
group: worker-image-${{ github.ref }}-${{ github.event.inputs.tag || 'cu128' }}
|
|
53
|
+
cancel-in-progress: true
|
|
54
|
+
|
|
44
55
|
jobs:
|
|
45
56
|
build:
|
|
46
57
|
runs-on: ubuntu-24.04-8core
|
|
58
|
+
# Single source for the FlashAttention wheels: used BOTH as build-args and as fingerprint inputs
|
|
59
|
+
# (so a wheel bump moves fp_base). Keep FA3_SPEC in sync with Dockerfile.worker's ARG default.
|
|
60
|
+
env:
|
|
61
|
+
FA2_SPEC: https://github.com/mjun0812/flash-attention-prebuild-wheels/releases/download/v0.9.0/flash_attn-2.8.3%2Bcu128torch2.10-cp312-cp312-linux_x86_64.whl
|
|
62
|
+
FA3_SPEC: ${{ github.event.inputs.flash_attn_3_spec || 'https://github.com/windreamer/flash-attention3-wheels/releases/download/2026.03.19-850211f/flash_attn_3-3.0.0%2B20260318.cu128torch2100cxx11abitrue.8afc61-cp39-abi3-linux_x86_64.whl' }}
|
|
47
63
|
steps:
|
|
48
64
|
- uses: actions/checkout@v6
|
|
49
65
|
|
|
66
|
+
# Compute the kernel-cache fingerprints (stdlib-only, no uv sync) and stamp them as labels on
|
|
67
|
+
# :cu128 below, so auto-rebake.yml can read what this base was built from. Fails the build if a
|
|
68
|
+
# pin can't be parsed -- a silent miss would let a stale per-arch cache ship as a cold-JIT.
|
|
69
|
+
- name: Compute kernel fingerprints
|
|
70
|
+
id: fp
|
|
71
|
+
run: python3 docker/kernel_fingerprint.py --format github --fa2-spec "$FA2_SPEC" --fa3-spec "$FA3_SPEC" >> "$GITHUB_OUTPUT"
|
|
72
|
+
|
|
50
73
|
- name: Free disk space
|
|
51
74
|
run: |
|
|
52
75
|
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/.ghcup \
|
|
@@ -77,9 +100,20 @@ jobs:
|
|
|
77
100
|
# so a fork run must not push to its own owner namespace and leave the real image stale.
|
|
78
101
|
tags: ghcr.io/freesolo-co/flash-worker:${{ github.event.inputs.tag || 'cu128' }}
|
|
79
102
|
provenance: false
|
|
103
|
+
# Stamp the kernel-cache fingerprints + the source commit so auto-rebake.yml can read what
|
|
104
|
+
# this base was built from (docker/kernel_fingerprint.py is the source of truth).
|
|
105
|
+
labels: |
|
|
106
|
+
co.freesolo.flash.kernelcache.fp_cache=${{ steps.fp.outputs.fp_cache }}
|
|
107
|
+
co.freesolo.flash.kernelcache.fp_base=${{ steps.fp.outputs.fp_base }}
|
|
108
|
+
org.opencontainers.image.revision=${{ github.sha }}
|
|
80
109
|
# Install a PREBUILT flash-attn wheel instead of compiling from source (a multi-arch
|
|
81
110
|
# source compile is huge and OOM-kills the runner). The wheel matches the base exactly
|
|
82
|
-
# (cu128 / torch2.10 / cp312).
|
|
111
|
+
# (cu128 / torch2.10 / cp312). FA specs come from the job-level env (single source).
|
|
83
112
|
build-args: |
|
|
84
|
-
FLASH_ATTN_SPEC
|
|
85
|
-
FLASH_ATTN_3_SPEC=${{
|
|
113
|
+
FLASH_ATTN_SPEC=${{ env.FA2_SPEC }}
|
|
114
|
+
FLASH_ATTN_3_SPEC=${{ env.FA3_SPEC }}
|
|
115
|
+
|
|
116
|
+
# When :cu128 is republished the per-arch cu128-sm* tags go stale. auto-rebake.yml now handles
|
|
117
|
+
# that automatically on this workflow's completion: a free re-layer when only the base moved, or
|
|
118
|
+
# a one-click GPU re-warm when the kernel toolchain changed (it reads the fp_cache/fp_base labels
|
|
119
|
+
# stamped above vs each published per-arch tag). The old manual reminder is gone.
|