freesolo-flash-dev 0.2.34__tar.gz → 0.2.35__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.34 → freesolo_flash_dev-0.2.35}/PKG-INFO +1 -1
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cli/__init__.py +17 -14
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cli/commands.py +78 -44
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cli/envpush.py +9 -11
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cli/render.py +22 -6
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cli/training_doc.py +8 -7
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/client/http.py +37 -26
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/vram.py +52 -3
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/adapter.py +14 -17
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/hf.py +123 -37
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/rl.py +3 -3
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/sft.py +1 -1
- freesolo_flash_dev-0.2.35/flash/envs/adapter.py +447 -0
- freesolo_flash_dev-0.2.34/flash/envs/adapter.py → freesolo_flash_dev-0.2.35/flash/envs/loader.py +98 -399
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/envs/pull.py +1 -1
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/train/endpoints.py +64 -29
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/runner/__init__.py +107 -12
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/runner/checkpoints.py +44 -14
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/runner/deploy.py +63 -11
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/schema/__init__.py +55 -5
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/serve/deploy.py +173 -13
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/app.py +18 -1
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/envs.py +3 -4
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/routes/runs.py +1 -1
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/routes/serving.py +225 -43
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/pyproject.toml +2 -2
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/conftest.py +29 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_agent_flash_cli_contract.py +2 -4
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cancel_remote.py +123 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_checkpoints.py +115 -1
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cli_commands.py +73 -14
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cli_render_theme.py +3 -5
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_client.py +31 -42
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_client_server_integration.py +6 -0
- freesolo_flash_dev-0.2.35/tests/test_env_cache_evict.py +70 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_env_pull.py +1 -1
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_env_rate_limit_resolve.py +8 -8
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_github_urlopen_retry.py +1 -1
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_gpus.py +18 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_grpo_params.py +54 -109
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_jobs.py +344 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_kv_util.py +8 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_orchestrator_flash.py +3 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_serve.py +45 -4
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_server_api.py +139 -13
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_serving_contract.py +191 -3
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_verifiers.py +137 -14
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_warmstart_cross_repo.py +21 -4
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/uv.lock +1 -1
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.dockerignore +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.env.example +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/auto-rebake.yml +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/bake-kernel-cache.yml +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/ci.yml +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/main-source-guard.yml +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/notify-tests-repo.yml +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/publish-dev.yml +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/publish-image.yml +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/publish.yml +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/version-parity.yml +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.github/workflows/worker-image.yml +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/.gitignore +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/Dockerfile +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/Dockerfile.worker +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/LICENSE +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/README.md +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/build/kernel_cache/.gitignore +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/build/kernel_cache/.keep +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docker/Dockerfile.kernelcache +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docker/Dockerfile.kernelcache.relayer +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docker/bake_kernel_cache.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docker/bake_pod_entry.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docker/kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docker/make_rp_handler.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docs/cli-style/README.md +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docs/cli-style/generate.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docs/cli-style/index.html +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docs/cli-style/preview.png +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docs/cli-style/themed-errors.png +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/docs/kernel-cache.md +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/_channel.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/_fileio.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/_logging.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/_update_check.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/catalog.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cli/__main__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/client/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/client/config.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/client/runtime_secrets.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/client/specs.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cost/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cost/analytical.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cost/facts.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cost/spec.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/cost/types.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/accounting.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/chalk_kernels.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/multiturn_rollout.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/recipe.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/__main__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/_pkg.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/decoding.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/finalize.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/gpu_setup.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/grpo.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/heartbeat.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/kernel_warmup.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/lora.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/packing.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/attn.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/diagnostics.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/lifecycle.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/liger.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/loraplus.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/perf/memory.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/engine/worker/wandb_log.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/envs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/envs/base.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/envs/registry.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/_auth.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/_http.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/_instance.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/_instance_bootstrap.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/_poll.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/allocator.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/base.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/api.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/auth.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/gpus.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/jobs/builders.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/preflight.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/pricing.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/lambdalabs/train.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/preflight.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/realized.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/api.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/auth.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/cost.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/gpus.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/jobs.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/keys.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/preflight.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/preload.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/pricing.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/slots.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/train/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/providers/runpod/train/deps.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/py.typed +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/runner/lifecycle.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/schema/fields.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/serve/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/serve/export.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/serve/pricing.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/__main__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/_deps.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/_internal_client.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/_locks.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/_runtime.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/auth.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/billing.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/billing_retry.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/checkpoints.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/db.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/environment_registry.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/reconcile.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/routes/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/routes/envs.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/routes/meta.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/server/run_registry.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/flash/spec.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/infisical-entrypoint.sh +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/scripts/build_dev_dist.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/_helpers/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/_helpers/runner.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/_helpers/specs.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/fixtures/math_eval.jsonl +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/fixtures/math_train.jsonl +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/live/__init__.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/live/conftest.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/live/test_lambda_live.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/live/test_runpod_live.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_algorithms.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_allocator.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_b200_rollout_opt.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_backend_jobspec_contract.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_billing_retry.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_catalog_consistency.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_chalk_kernels.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_charge_pricing.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cli_errors.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cli_estimate.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cli_help.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cli_managed.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_config_overrides.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cost_analytical.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cost_equation.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cost_estimate.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cost_hardware.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cost_models.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_cost_rewards.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_dev_channel.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_disk_gb.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_endpoint_name.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_env_delete.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_env_no_install.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_env_publish.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_env_push.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_envs_coverage.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_export.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_flash_mvp.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_flash_worker.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_flashinfer_cache_dirs.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_grpo_mask_aware.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_grpo_sleep_gate.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_idle_endpoint_reaper.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_internal_client.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_kernel_cache.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_lambda_runner.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_logging.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_login_perms.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_managed_hf_repo.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_metrics_schema_agent_contract.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_mig_guard.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_multiturn_rollout.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_oom_escalate_gpu.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_open_model_policy.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_packing.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_poll_helpers.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_preflight.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_provider_routing.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_provider_teardown_robustness.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_providers_symmetry.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_realized_cost.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_resolve_params_b.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_resume_on_retry.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_runmgmt.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_runpod_api_delete.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_runpod_key_fingerprint.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_runpod_key_waterfall.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_runpod_slots.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_server_billing.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_server_db.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_sft_gc_off.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_spec_and_validation.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_thinking_config.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_update_check.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_version.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_vl_warmstart_adapter_keys.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_vl_warmstart_recombine.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_vl_weight_sync.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_wandb_naming.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_weight_cache.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_worker_dryrun.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_worker_hardexit.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_worker_image.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_worker_init_heartbeat.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/tests/test_worker_stack.py +0 -0
- {freesolo_flash_dev-0.2.34 → freesolo_flash_dev-0.2.35}/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.35
|
|
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
|
|
@@ -348,14 +348,17 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
348
348
|
checkpoints.set_defaults(func=cmd_checkpoints)
|
|
349
349
|
|
|
350
350
|
deploy = sub.add_parser("deploy", help="deploy a run's adapter to a serving endpoint")
|
|
351
|
-
deploy.add_argument(
|
|
351
|
+
deploy.add_argument(
|
|
352
|
+
"run_id",
|
|
353
|
+
metavar="RUN_ID[/step-N]",
|
|
354
|
+
help="run id for the final adapter, or RUN_ID/step-N for a saved checkpoint",
|
|
355
|
+
)
|
|
352
356
|
deploy.add_argument("--dry-run", action="store_true")
|
|
353
357
|
deploy.add_argument(
|
|
354
|
-
"--
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
"instead of the run's final adapter; works even for a run cancelled mid-RL",
|
|
358
|
+
"--no-verify",
|
|
359
|
+
action="store_true",
|
|
360
|
+
help="skip the server-side post-deploy smoke generation (registration alone does NOT "
|
|
361
|
+
"guarantee the adapter serves; only ready deployments should be scored by evals)",
|
|
359
362
|
)
|
|
360
363
|
deploy.set_defaults(func=cmd_deploy)
|
|
361
364
|
|
|
@@ -368,7 +371,8 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
368
371
|
"--adapter-id",
|
|
369
372
|
dest="adapter_id",
|
|
370
373
|
required=True,
|
|
371
|
-
|
|
374
|
+
metavar="RUN_ID[/step-N]",
|
|
375
|
+
help="run id for the final adapter, or RUN_ID/step-N for a saved checkpoint",
|
|
372
376
|
)
|
|
373
377
|
export.add_argument(
|
|
374
378
|
"--repository",
|
|
@@ -380,13 +384,6 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
380
384
|
help="HuggingFace token with write access to --repository "
|
|
381
385
|
"(default: HF_TOKEN from your shell or a local .env / .env.local)",
|
|
382
386
|
)
|
|
383
|
-
export.add_argument(
|
|
384
|
-
"--step",
|
|
385
|
-
type=int,
|
|
386
|
-
default=None,
|
|
387
|
-
help="export a specific intermediate checkpoint (see `flash checkpoints <adapter-id>`) "
|
|
388
|
-
"instead of the run's final adapter; works even for a run cancelled mid-RL",
|
|
389
|
-
)
|
|
390
387
|
export.add_argument(
|
|
391
388
|
"--public",
|
|
392
389
|
action="store_true",
|
|
@@ -400,6 +397,12 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
400
397
|
chat = sub.add_parser("chat", help="chat with a deployed adapter")
|
|
401
398
|
chat.add_argument("run_id")
|
|
402
399
|
chat.add_argument("-m", "--message", required=True)
|
|
400
|
+
chat.add_argument(
|
|
401
|
+
"--system",
|
|
402
|
+
default=None,
|
|
403
|
+
help="optional system prompt sent ahead of the user message "
|
|
404
|
+
"(training-prompt parity for evals)",
|
|
405
|
+
)
|
|
403
406
|
chat.add_argument("--max-tokens", type=int, default=512)
|
|
404
407
|
chat.add_argument("--temperature", type=float, default=0.0)
|
|
405
408
|
chat.set_defaults(func=cmd_chat)
|
|
@@ -407,9 +407,12 @@ def cmd_train(args) -> int:
|
|
|
407
407
|
return _follow_run(client, run_id)
|
|
408
408
|
|
|
409
409
|
|
|
410
|
-
def _poll_logs(client: ApiClient, run_id: str, interval: float) -> str:
|
|
411
|
-
"""Stream offset-paged logs until the run reaches a terminal state
|
|
410
|
+
def _poll_logs(client: ApiClient, run_id: str, interval: float) -> tuple[str, bool]:
|
|
411
|
+
"""Stream offset-paged logs until the run reaches a terminal state.
|
|
412
|
+
|
|
413
|
+
Returns (terminal state, whether any log bytes were printed)."""
|
|
412
414
|
offset = 0
|
|
415
|
+
printed_any = False
|
|
413
416
|
spinner = _LogFollowSpinner(run_id)
|
|
414
417
|
try:
|
|
415
418
|
while True:
|
|
@@ -417,23 +420,25 @@ def _poll_logs(client: ApiClient, run_id: str, interval: float) -> str:
|
|
|
417
420
|
if page["logs"]:
|
|
418
421
|
spinner.clear()
|
|
419
422
|
print(page["logs"], end="", flush=True)
|
|
423
|
+
printed_any = True
|
|
420
424
|
offset = page["offset"]
|
|
421
425
|
if page["state"] in _CLI_DONE_STATES:
|
|
422
426
|
spinner.clear()
|
|
423
|
-
return page["state"]
|
|
427
|
+
return page["state"], printed_any
|
|
424
428
|
_sleep_with_spinner(interval, spinner, page["state"])
|
|
425
429
|
finally:
|
|
426
430
|
spinner.clear()
|
|
427
431
|
|
|
428
432
|
|
|
433
|
+
def _render_status(status: dict) -> str:
|
|
434
|
+
"""One rendering of a run status: themed panel on a TTY, indented JSON on the machine path."""
|
|
435
|
+
return render.run_status(status) if render.styled() else json.dumps(status, indent=2)
|
|
436
|
+
|
|
437
|
+
|
|
429
438
|
def _follow_run(client: ApiClient, run_id: str) -> int:
|
|
430
439
|
"""Poll logs until the run reaches a terminal state, then print the final status."""
|
|
431
|
-
state = _poll_logs(client, run_id, interval=2.0)
|
|
432
|
-
|
|
433
|
-
if render.styled():
|
|
434
|
-
print(render.run_status(status))
|
|
435
|
-
else:
|
|
436
|
-
print(json.dumps(status, indent=2))
|
|
440
|
+
state, _ = _poll_logs(client, run_id, interval=2.0)
|
|
441
|
+
print(_render_status(client.get_run(run_id)))
|
|
437
442
|
return 0 if state in _OK_STATES else 1
|
|
438
443
|
|
|
439
444
|
|
|
@@ -442,7 +447,7 @@ def _follow_status(client: ApiClient, run_id: str, interval: float = 2.0) -> int
|
|
|
442
447
|
last_rendered: str | None = None
|
|
443
448
|
while True:
|
|
444
449
|
status = client.get_run(run_id)
|
|
445
|
-
rendered =
|
|
450
|
+
rendered = _render_status(status)
|
|
446
451
|
if rendered != last_rendered:
|
|
447
452
|
print(rendered)
|
|
448
453
|
last_rendered = rendered
|
|
@@ -452,21 +457,6 @@ def _follow_status(client: ApiClient, run_id: str, interval: float = 2.0) -> int
|
|
|
452
457
|
time.sleep(interval)
|
|
453
458
|
|
|
454
459
|
|
|
455
|
-
def _print_log_text(text: str) -> bool:
|
|
456
|
-
if not text:
|
|
457
|
-
return False
|
|
458
|
-
print(text, end="")
|
|
459
|
-
if not text.endswith("\n"):
|
|
460
|
-
print()
|
|
461
|
-
return True
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
def _print_log_snapshot(client: ApiClient, run_id: str) -> bool:
|
|
465
|
-
"""Print a finite snapshot from the log endpoint without following new bytes."""
|
|
466
|
-
page = client.get_logs(run_id, offset=0)
|
|
467
|
-
return _print_log_text(str(page.get("logs") or ""))
|
|
468
|
-
|
|
469
|
-
|
|
470
460
|
def _print_worker_output(client: ApiClient, run_id: str, *, printed_any: bool = False) -> bool:
|
|
471
461
|
for name, text in (client.get_worker_output(run_id) or {}).items():
|
|
472
462
|
if not text:
|
|
@@ -484,11 +474,13 @@ def _print_worker_output(client: ApiClient, run_id: str, *, printed_any: bool =
|
|
|
484
474
|
def cmd_log(args) -> int:
|
|
485
475
|
client = client_from_config()
|
|
486
476
|
if getattr(args, "follow", False):
|
|
487
|
-
state = _poll_logs(client, args.run_id, interval=2.0)
|
|
488
|
-
_print_worker_output(client, args.run_id, printed_any=
|
|
477
|
+
state, printed_any = _poll_logs(client, args.run_id, interval=2.0)
|
|
478
|
+
_print_worker_output(client, args.run_id, printed_any=printed_any)
|
|
489
479
|
return 0 if state in _OK_STATES else 1
|
|
490
|
-
|
|
491
|
-
|
|
480
|
+
text = str(client.get_logs(args.run_id, offset=0).get("logs") or "")
|
|
481
|
+
if text:
|
|
482
|
+
print(text, end="" if text.endswith("\n") else "\n")
|
|
483
|
+
_print_worker_output(client, args.run_id, printed_any=bool(text))
|
|
492
484
|
return 0
|
|
493
485
|
|
|
494
486
|
|
|
@@ -496,11 +488,7 @@ def cmd_status(args) -> int:
|
|
|
496
488
|
client = client_from_config()
|
|
497
489
|
if getattr(args, "follow", False):
|
|
498
490
|
return _follow_status(client, args.run_id)
|
|
499
|
-
|
|
500
|
-
if render.styled():
|
|
501
|
-
print(render.run_status(status))
|
|
502
|
-
else:
|
|
503
|
-
print(json.dumps(status, indent=2))
|
|
491
|
+
print(_render_status(client.get_run(args.run_id)))
|
|
504
492
|
return 0
|
|
505
493
|
|
|
506
494
|
|
|
@@ -553,20 +541,37 @@ def cmd_checkpoints(args) -> int:
|
|
|
553
541
|
if render.styled():
|
|
554
542
|
print(render.checkpoints_table(args.run_id, checkpoints))
|
|
555
543
|
return 0
|
|
544
|
+
from flash.schema import format_checkpoint_ref
|
|
545
|
+
|
|
556
546
|
for c in checkpoints:
|
|
557
|
-
|
|
547
|
+
# single-space, unpadded columns so a plain `grep "step N"` / awk split works; the ref is
|
|
548
|
+
# the canonical short form, paste-able into train.init_from_adapter.
|
|
549
|
+
print(f"step {c['step']} {format_checkpoint_ref(args.run_id, c['step'])}")
|
|
558
550
|
print(
|
|
559
|
-
f"\ndeploy one with `flash deploy {args.run_id}
|
|
551
|
+
f"\ndeploy one with `flash deploy {args.run_id}/step-<STEP>`.",
|
|
560
552
|
file=sys.stderr,
|
|
561
553
|
)
|
|
562
554
|
return 0
|
|
563
555
|
|
|
564
556
|
|
|
565
557
|
def cmd_deploy(args) -> int:
|
|
566
|
-
|
|
558
|
+
from flash.schema import parse_checkpoint_ref
|
|
559
|
+
|
|
560
|
+
# `flash deploy <run_id>/step-N` is the same checkpoint ref `flash checkpoints` prints.
|
|
561
|
+
parsed = parse_checkpoint_ref(args.run_id)
|
|
562
|
+
if parsed is None:
|
|
563
|
+
print(
|
|
564
|
+
f"invalid run/checkpoint reference {args.run_id!r} "
|
|
565
|
+
"(expected <run_id> or <run_id>/step-N)",
|
|
566
|
+
file=sys.stderr,
|
|
567
|
+
)
|
|
568
|
+
return 1
|
|
569
|
+
base_run_id, _step = parsed
|
|
570
|
+
client = client_from_config()
|
|
571
|
+
dep = client.deploy(
|
|
567
572
|
args.run_id,
|
|
568
573
|
dry_run=args.dry_run,
|
|
569
|
-
|
|
574
|
+
verify=not getattr(args, "no_verify", False),
|
|
570
575
|
)
|
|
571
576
|
if render.styled():
|
|
572
577
|
print(render.deployed(dep))
|
|
@@ -574,11 +579,35 @@ def cmd_deploy(args) -> int:
|
|
|
574
579
|
print(json.dumps(dep, indent=2))
|
|
575
580
|
# a dry run creates no deployment, so the billing / undeploy hint would be misleading.
|
|
576
581
|
if dep.get("state") != "dry_run":
|
|
582
|
+
endpoint = str(dep.get("endpoint_name") or "").rstrip("/")
|
|
583
|
+
openai_base = dep.get("url") or (f"{endpoint}/v1" if endpoint else "")
|
|
577
584
|
note = (
|
|
578
|
-
f"serving is billed per token only; use `flash undeploy {
|
|
585
|
+
f"serving is billed per token only; use `flash undeploy {base_run_id}` "
|
|
579
586
|
"to deregister the adapter."
|
|
580
587
|
)
|
|
581
588
|
print(render.arrow(note) if render.styled() else f"note: {note}", file=sys.stderr)
|
|
589
|
+
if openai_base:
|
|
590
|
+
url_note = (
|
|
591
|
+
f"OpenAI-compatible base URL: {openai_base} — point clients at this /v1 base, "
|
|
592
|
+
"not the bare endpoint (which 404s on /chat/completions)."
|
|
593
|
+
)
|
|
594
|
+
print(render.arrow(url_note) if render.styled() else f"note: {url_note}", file=sys.stderr)
|
|
595
|
+
if dep.get("state") != "dry_run":
|
|
596
|
+
state = dep.get("state", "deploying")
|
|
597
|
+
status_note = (
|
|
598
|
+
f"deployment state is {state!r}; run `flash deployments` to check progress "
|
|
599
|
+
"and use `flash chat` once it is ready."
|
|
600
|
+
)
|
|
601
|
+
print(
|
|
602
|
+
render.arrow(status_note) if render.styled() else f"note: {status_note}",
|
|
603
|
+
file=sys.stderr,
|
|
604
|
+
)
|
|
605
|
+
if getattr(args, "no_verify", False):
|
|
606
|
+
skip_note = "server-side smoke verification was skipped for this deployment."
|
|
607
|
+
print(
|
|
608
|
+
render.arrow(skip_note) if render.styled() else f"note: {skip_note}",
|
|
609
|
+
file=sys.stderr,
|
|
610
|
+
)
|
|
582
611
|
return 0
|
|
583
612
|
|
|
584
613
|
|
|
@@ -592,9 +621,8 @@ def cmd_export(args) -> int:
|
|
|
592
621
|
"(export it in your shell or put it in a local .env / .env.local)"
|
|
593
622
|
)
|
|
594
623
|
client = client_from_config()
|
|
595
|
-
where = f" (step {args.step})" if args.step is not None else ""
|
|
596
624
|
progress = (
|
|
597
|
-
f"exporting adapter {args.adapter_id}
|
|
625
|
+
f"exporting adapter {args.adapter_id} to {args.repository} — "
|
|
598
626
|
"downloading then re-uploading; this can take a minute..."
|
|
599
627
|
)
|
|
600
628
|
print(render.note(progress) if render.styled() else progress, file=sys.stderr)
|
|
@@ -602,7 +630,6 @@ def cmd_export(args) -> int:
|
|
|
602
630
|
args.adapter_id,
|
|
603
631
|
repository=args.repository,
|
|
604
632
|
hf_token=hf_token,
|
|
605
|
-
step=args.step,
|
|
606
633
|
private=not args.public,
|
|
607
634
|
)
|
|
608
635
|
if render.styled():
|
|
@@ -639,16 +666,23 @@ def cmd_deployments(args) -> int:
|
|
|
639
666
|
if render.styled():
|
|
640
667
|
print(render.deployments_table(rows))
|
|
641
668
|
return 0
|
|
642
|
-
print(f"{'RUN_ID':<32} {'GPU':<9} ENDPOINT")
|
|
669
|
+
print(f"{'RUN_ID':<32} {'STATE':<10} {'GPU':<9} {'ENDPOINT':<32} DETAIL")
|
|
643
670
|
for r in rows:
|
|
644
671
|
d = r.get("deployment") or {}
|
|
645
|
-
|
|
672
|
+
detail = str(d.get("error") or d.get("detail") or "")
|
|
673
|
+
print(
|
|
674
|
+
f"{r['run_id']:<32} {d.get('state', '?'):<10} "
|
|
675
|
+
f"{d.get('gpu', '?'):<9} {d.get('endpoint_name', ''):<32} {detail}"
|
|
676
|
+
)
|
|
646
677
|
return 0
|
|
647
678
|
|
|
648
679
|
|
|
649
680
|
def cmd_chat(args) -> int:
|
|
650
681
|
client = client_from_config()
|
|
651
682
|
messages = [{"role": "user", "content": args.message}]
|
|
683
|
+
system = getattr(args, "system", None)
|
|
684
|
+
if system:
|
|
685
|
+
messages.insert(0, {"role": "system", "content": system})
|
|
652
686
|
if render.styled():
|
|
653
687
|
print(render.chat_label())
|
|
654
688
|
stream = getattr(client, "chat_stream", None)
|
|
@@ -188,25 +188,23 @@ _ENV_PUSH_SIDECAR_SUFFIXES = frozenset(
|
|
|
188
188
|
|
|
189
189
|
|
|
190
190
|
def _normalize_env_name(raw: str) -> str | None:
|
|
191
|
-
|
|
191
|
+
"""Normalize only the NAME segment; a namespace prefix is passed through verbatim.
|
|
192
192
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
return segment
|
|
193
|
+
The server is the sole authority on namespace grammar and ownership — rewriting the
|
|
194
|
+
namespace client-side would silently target a different (possibly forbidden) slug.
|
|
195
|
+
"""
|
|
196
|
+
from flash.schema import normalize_env_name_segment
|
|
198
197
|
|
|
199
198
|
text = str(raw or "").strip()
|
|
200
199
|
if "/" not in text:
|
|
201
|
-
return
|
|
200
|
+
return normalize_env_name_segment(text)
|
|
202
201
|
parts = [part.strip() for part in text.split("/")]
|
|
203
202
|
if len(parts) != 2 or not all(parts):
|
|
204
203
|
return None
|
|
205
|
-
|
|
206
|
-
name
|
|
207
|
-
if not namespace or not name:
|
|
204
|
+
name = normalize_env_name_segment(parts[1])
|
|
205
|
+
if name is None:
|
|
208
206
|
return None
|
|
209
|
-
return f"{
|
|
207
|
+
return f"{parts[0]}/{name}"
|
|
210
208
|
|
|
211
209
|
|
|
212
210
|
def _with_syspath_bootstrap(env_source: str) -> str:
|
|
@@ -439,28 +439,37 @@ def deployments_table(rows: list[dict]) -> str:
|
|
|
439
439
|
body = []
|
|
440
440
|
for r in rows:
|
|
441
441
|
d = r.get("deployment") or {}
|
|
442
|
+
state = str(d.get("state") or "?")
|
|
443
|
+
color = _GREEN if state in {"ready", "deployed"} else _RED if state == "failed" else _AMBER
|
|
444
|
+
detail = str(d.get("error") or d.get("detail") or "")
|
|
445
|
+
if len(detail) > 64:
|
|
446
|
+
detail = detail[:61] + "..."
|
|
442
447
|
body.append(
|
|
443
448
|
[
|
|
444
449
|
(r["run_id"], _ACCENT2),
|
|
450
|
+
(state, color),
|
|
445
451
|
(d.get("gpu", "?"), _GRAY),
|
|
446
452
|
(d.get("endpoint_name", ""), _GREEN),
|
|
453
|
+
(detail, _GRAY),
|
|
447
454
|
]
|
|
448
455
|
)
|
|
449
|
-
table = _table(["RUN ID", "GPU", "ENDPOINT"], body)
|
|
456
|
+
table = _table(["RUN ID", "STATE", "GPU", "ENDPOINT", "DETAIL"], body)
|
|
450
457
|
return _safe(f"{header('deployments', f'{len(rows)} active')}\n{table}")
|
|
451
458
|
|
|
452
459
|
|
|
453
460
|
def checkpoints_table(run_id: str, rows: list[dict]) -> str:
|
|
454
|
-
"""Deployable per-step RL checkpoints:
|
|
461
|
+
"""Deployable per-step RL checkpoints: step number + the canonical `<run_id>/step-N` ref."""
|
|
462
|
+
from flash.schema import format_checkpoint_ref
|
|
463
|
+
|
|
455
464
|
body = [
|
|
456
465
|
[
|
|
457
466
|
(str(c.get("step", "")), _TEAL),
|
|
458
|
-
(
|
|
467
|
+
(format_checkpoint_ref(run_id, c.get("step", 0)), _ACCENT2),
|
|
459
468
|
]
|
|
460
469
|
for c in sorted(rows, key=lambda c: c.get("step", 0))
|
|
461
470
|
]
|
|
462
471
|
table = _table(["STEP", "CHECKPOINT"], body, aligns=["r", "l"])
|
|
463
|
-
foot = arrow(f"deploy one with: flash deploy {run_id}
|
|
472
|
+
foot = arrow(f"deploy one with: flash deploy {run_id}/step-<STEP>")
|
|
464
473
|
return _safe(f"{header('checkpoints', f'{len(rows)} deployable')}\n{table}\n\n{foot}")
|
|
465
474
|
|
|
466
475
|
|
|
@@ -541,11 +550,14 @@ def cancelled(payload: dict) -> str:
|
|
|
541
550
|
|
|
542
551
|
def deployed(dep: dict) -> str:
|
|
543
552
|
"""`flash deploy`: the endpoint, gpu, and serving url as an aligned card (not a JSON dump)."""
|
|
553
|
+
endpoint = str(dep.get("endpoint_name") or "")
|
|
554
|
+
# OpenAI clients need the /v1 base; older servers omit `url`, so derive it from the endpoint.
|
|
555
|
+
url = dep.get("url") or (f"{endpoint.rstrip('/')}/v1" if endpoint else None)
|
|
544
556
|
pairs = [
|
|
545
557
|
("run", _paint(dep.get("run_id", ""), _ACCENT2)),
|
|
546
|
-
("endpoint", _paint(
|
|
558
|
+
("endpoint", _paint(endpoint, _GREEN) if endpoint else None),
|
|
547
559
|
("gpu", dep.get("gpu")),
|
|
548
|
-
("url", _paint(
|
|
560
|
+
("url", _paint(url, _ACCENT2) if url else None),
|
|
549
561
|
]
|
|
550
562
|
state = dep.get("state", "deployed")
|
|
551
563
|
if state == "dry_run":
|
|
@@ -571,6 +583,10 @@ def undeployed(result: dict) -> str:
|
|
|
571
583
|
line = ok(f"torn down {rid}")
|
|
572
584
|
if deleted:
|
|
573
585
|
line += "\n" + _dim(f" deregistered {', '.join(deleted)}")
|
|
586
|
+
else:
|
|
587
|
+
line += "\n" + _dim(
|
|
588
|
+
" serving had no registered adapter to deregister (already gone or never registered)"
|
|
589
|
+
)
|
|
574
590
|
return _safe(line)
|
|
575
591
|
|
|
576
592
|
|
|
@@ -138,7 +138,8 @@ flash cancel <run-id> # stop a run
|
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
140
|
flash checkpoints <run-id> # deployable per-step RL checkpoints
|
|
141
|
-
flash deploy <run-id> # serve the trained adapter
|
|
141
|
+
flash deploy <run-id> # serve the trained adapter
|
|
142
|
+
flash deploy <run-id>/step-N # serve an intermediate checkpoint
|
|
142
143
|
flash chat <run-id> -m "hello" # chat with the deployed adapter
|
|
143
144
|
flash deployments # active serving endpoints
|
|
144
145
|
flash undeploy <run-id> # tear the endpoint down
|
|
@@ -208,7 +209,7 @@ spending another GPU run:
|
|
|
208
209
|
| 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. |
|
|
209
210
|
| 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 log <run-id>`, reduce footprint (`max_length`, `max_tokens`, `group_size`) if needed, and let Flash retry/allocate another fitting GPU class. |
|
|
210
211
|
| Run looks stuck after disconnecting | Terminal stopped streaming but the job may still be alive | Ctrl-C detaches. Use `flash log <run-id> --follow` to reattach, `flash log <run-id>` for the console/error output, or `flash cancel <run-id>` if you intentionally want to stop it. |
|
|
211
|
-
| 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
|
|
212
|
+
| 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. |
|
|
212
213
|
| 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`. |
|
|
213
214
|
| 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. |
|
|
214
215
|
| 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. |
|
|
@@ -350,9 +351,9 @@ Pick SFT when you already have good answers and want the model to imitate them.
|
|
|
350
351
|
algorithm = "grpo"
|
|
351
352
|
|
|
352
353
|
[train]
|
|
353
|
-
#
|
|
354
|
-
#
|
|
355
|
-
init_from_adapter = "
|
|
354
|
+
# the SFT run id (as printed by `flash status`); add /step-N to warm-start from a
|
|
355
|
+
# specific checkpoint listed by `flash checkpoints <run-id>`
|
|
356
|
+
init_from_adapter = "<sft-run-id>"
|
|
356
357
|
lora_rank = 16 # for VL warm-starts, SFT rank + GRPO rank must fit the effective serving cap
|
|
357
358
|
lora_alpha = 32
|
|
358
359
|
```
|
|
@@ -479,12 +480,12 @@ flash runs # list your runs and their state/cost
|
|
|
479
480
|
flash cancel <run-id> # stop a live run
|
|
480
481
|
flash checkpoints <run-id> # list deployable RL checkpoints
|
|
481
482
|
flash deploy <run-id> # serve the trained adapter
|
|
482
|
-
flash deploy <run-id
|
|
483
|
+
flash deploy <run-id>/step-N # serve a specific RL checkpoint
|
|
483
484
|
flash chat <run-id> -m "probe" # stream a reply from the deployed adapter
|
|
484
485
|
flash deployments # list active serving deployments
|
|
485
486
|
flash undeploy <run-id> # tear down an active deployment
|
|
486
487
|
flash export --adapter-id <run-id> --repository <you>/<repo> # export final adapter
|
|
487
|
-
flash export --adapter-id <run-id
|
|
488
|
+
flash export --adapter-id <run-id>/step-N --repository <you>/<repo> # export a checkpoint
|
|
488
489
|
```
|
|
489
490
|
|
|
490
491
|
See the full reference at https://freesolo.co/docs.
|
|
@@ -98,18 +98,6 @@ class _ProgressReader:
|
|
|
98
98
|
return chunk
|
|
99
99
|
|
|
100
100
|
|
|
101
|
-
def _validate_checkpoint_step(step: int | float) -> int:
|
|
102
|
-
"""Validate a checkpoint step before the request: reject bool (int(True)==1) and fractional floats
|
|
103
|
-
(int(2.7)==2 would target the WRONG checkpoint), then return the int — fail fast before the server."""
|
|
104
|
-
if isinstance(step, bool):
|
|
105
|
-
raise ClientError(f"invalid checkpoint step: {step!r} (must be an integer)")
|
|
106
|
-
if isinstance(step, float) and not step.is_integer():
|
|
107
|
-
raise ClientError(
|
|
108
|
-
f"invalid checkpoint step: {step!r} (must be a whole number, not fractional)"
|
|
109
|
-
)
|
|
110
|
-
return int(step)
|
|
111
|
-
|
|
112
|
-
|
|
113
101
|
def _validate_chat_messages(messages: list[dict]) -> None:
|
|
114
102
|
if not isinstance(messages, list):
|
|
115
103
|
raise ClientError("chat messages must be a list")
|
|
@@ -233,31 +221,45 @@ class ApiClient:
|
|
|
233
221
|
return self._request("GET", f"/v1/runs/{run_id}/logs?offset={int(offset)}")
|
|
234
222
|
|
|
235
223
|
def get_worker_output(self, run_id: str) -> dict[str, str]:
|
|
236
|
-
|
|
224
|
+
try:
|
|
225
|
+
return self._request("GET", f"/v1/runs/{run_id}/worker").get("worker", {})
|
|
226
|
+
except ApiError as exc:
|
|
227
|
+
if exc.status == 404 and "not found" in str(exc).strip().lower():
|
|
228
|
+
return {}
|
|
229
|
+
raise
|
|
237
230
|
|
|
238
231
|
def cancel_run(self, run_id: str) -> dict:
|
|
239
|
-
|
|
232
|
+
# Cancel blocks on synchronous provider teardown (worker stop + instance destroy +
|
|
233
|
+
# endpoint GC), which can far exceed the default 60s — timing out here left the CLI
|
|
234
|
+
# printing a traceback while the server went on to mark the run cancelled anyway.
|
|
235
|
+
return self._request("POST", f"/v1/runs/{run_id}/cancel", timeout=10 * 60)
|
|
240
236
|
|
|
241
237
|
def checkpoints(self, run_id: str) -> list[dict]:
|
|
242
|
-
"""Deployable per-step RL checkpoints for a run (
|
|
238
|
+
"""Deployable per-step RL checkpoints for a run (serve one with `flash deploy RUN/step-N`)."""
|
|
243
239
|
return self._request("GET", f"/v1/runs/{run_id}/checkpoints")["checkpoints"]
|
|
244
240
|
|
|
245
241
|
def deploy(
|
|
246
242
|
self,
|
|
247
243
|
run_id: str,
|
|
248
244
|
dry_run: bool = False,
|
|
249
|
-
|
|
245
|
+
verify: bool = True,
|
|
250
246
|
) -> dict:
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
247
|
+
from flash.schema import parse_checkpoint_ref
|
|
248
|
+
|
|
249
|
+
parsed = parse_checkpoint_ref(run_id)
|
|
250
|
+
if parsed is None:
|
|
251
|
+
raise ClientError(
|
|
252
|
+
"invalid adapter id: expected RUN_ID for the final adapter or RUN_ID/step-N "
|
|
253
|
+
"for a saved checkpoint"
|
|
254
|
+
)
|
|
255
|
+
base_run_id, step = parsed
|
|
256
|
+
body: dict = {"dry_run": dry_run, "verify": verify}
|
|
254
257
|
if step is not None:
|
|
255
|
-
body["step"] =
|
|
258
|
+
body["step"] = step
|
|
256
259
|
return self._request(
|
|
257
260
|
"POST",
|
|
258
|
-
f"/v1/runs/{
|
|
261
|
+
f"/v1/runs/{base_run_id}/deploy",
|
|
259
262
|
body=body,
|
|
260
|
-
timeout=deploy_timeout,
|
|
261
263
|
)
|
|
262
264
|
|
|
263
265
|
def export(
|
|
@@ -266,15 +268,23 @@ class ApiClient:
|
|
|
266
268
|
*,
|
|
267
269
|
repository: str,
|
|
268
270
|
hf_token: str,
|
|
269
|
-
step: int | None = None,
|
|
270
271
|
private: bool = True,
|
|
271
272
|
) -> dict:
|
|
272
273
|
"""Copy a run's adapter into a user-owned HuggingFace repo."""
|
|
274
|
+
from flash.schema import parse_checkpoint_ref
|
|
275
|
+
|
|
276
|
+
parsed = parse_checkpoint_ref(run_id)
|
|
277
|
+
if parsed is None:
|
|
278
|
+
raise ClientError(
|
|
279
|
+
"invalid adapter id: expected RUN_ID for the final adapter or RUN_ID/step-N "
|
|
280
|
+
"for a saved checkpoint"
|
|
281
|
+
)
|
|
282
|
+
base_run_id, step = parsed
|
|
273
283
|
body: dict = {"repository": repository, "hf_token": hf_token, "private": private}
|
|
274
284
|
if step is not None:
|
|
275
|
-
body["step"] =
|
|
285
|
+
body["step"] = step
|
|
276
286
|
return self._request(
|
|
277
|
-
"POST", f"/v1/runs/{
|
|
287
|
+
"POST", f"/v1/runs/{base_run_id}/export", body=body, timeout=30 * 60
|
|
278
288
|
)
|
|
279
289
|
|
|
280
290
|
def undeploy(self, run_id: str) -> dict:
|
|
@@ -289,13 +299,14 @@ class ApiClient:
|
|
|
289
299
|
messages: list[dict],
|
|
290
300
|
temperature: float = 0.0,
|
|
291
301
|
max_tokens: int = 512,
|
|
302
|
+
timeout: float | None = None,
|
|
292
303
|
) -> dict:
|
|
293
304
|
_validate_chat_messages(messages)
|
|
294
305
|
return self._request(
|
|
295
306
|
"POST",
|
|
296
307
|
f"/v1/runs/{run_id}/chat",
|
|
297
308
|
body={"messages": messages, "temperature": temperature, "max_tokens": max_tokens},
|
|
298
|
-
timeout=30 * 60,
|
|
309
|
+
timeout=timeout if timeout is not None else 30 * 60,
|
|
299
310
|
)
|
|
300
311
|
|
|
301
312
|
def chat_stream(
|