freesolo-flash-dev 1.0.0__tar.gz → 1.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/PKG-INFO +1 -1
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/catalog.py +25 -6
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/cli/commands.py +17 -13
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/client/http.py +2 -2
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/opd_vllm.py +8 -1
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/runner/__init__.py +2 -2
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/schema/__init__.py +9 -1
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/serve/export.py +147 -4
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/_deps.py +9 -13
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/repo_cleanup.py +23 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/routes/runs.py +18 -3
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/pyproject.toml +2 -2
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_catalog_consistency.py +30 -8
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_cli_commands.py +21 -1
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_cli_estimate.py +24 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_client.py +2 -2
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_export.py +363 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_jobs.py +7 -3
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_opd.py +45 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_opd_vllm.py +67 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_server_api.py +120 -7
- freesolo_flash_dev-1.0.1/tests/test_serving_context_preflight.py +39 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_train_context_preflight.py +6 -6
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_warmstart_cross_repo.py +2 -2
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/uv.lock +1 -1
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/.claude/skills/verify/SKILL.md +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/.dockerignore +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/.env.example +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/.github/workflows/auto-rebake.yml +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/.github/workflows/bake-kernel-cache.yml +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/.github/workflows/ci.yml +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/.github/workflows/main-source-guard.yml +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/.github/workflows/notify-tests-repo.yml +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/.github/workflows/publish-dev.yml +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/.github/workflows/publish-image.yml +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/.github/workflows/publish.yml +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/.github/workflows/version-parity.yml +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/.github/workflows/worker-image.yml +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/.gitignore +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/Dockerfile +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/Dockerfile.worker +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/LICENSE +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/README.md +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/build/kernel_cache/.gitignore +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/build/kernel_cache/.keep +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/docker/Dockerfile.kernelcache +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/docker/Dockerfile.kernelcache.relayer +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/docker/bake_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/docker/bake_pod_entry.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/docker/kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/docker/make_rp_handler.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/_channel.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/_fileio.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/_logging.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/_update_check.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/adapter_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/cli/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/cli/__main__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/cli/_tty.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/cli/env_setup.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/cli/envpush.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/cli/render.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/cli/training_doc.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/client/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/client/config.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/client/runtime_secrets.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/client/specs.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/cost/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/cost/analytical.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/cost/facts.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/cost/spec.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/cost/types.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/accounting.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/chalk_kernels.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/multiturn_rollout.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/recipe.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/steps.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/vram.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/__main__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/_pkg.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/adapter.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/decoding.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/finalize.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/gpu_setup.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/grpo.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/heartbeat.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/hf.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/kernel_warmup.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/lora.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/opd.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/opd_gkd.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/packing.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/perf/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/perf/attn.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/perf/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/perf/lifecycle.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/perf/liger.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/perf/loraplus.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/perf/memory.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/rl.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/rng.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/sft.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/teacher.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/tokenizer_align.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker/wandb_log.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/engine/worker_entrypoint.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/envs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/envs/adapter.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/envs/archive.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/envs/archive_policy.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/envs/base.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/envs/loader.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/envs/pull.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/envs/registry.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/lora_rank.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/opd_retry_contract.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/_auth.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/_deadline.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/_hf_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/_http.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/_instance.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/_instance_bootstrap.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/_instance_poll.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/_instance_provider.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/_poll.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/_worker.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/allocator.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/base.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/lambdalabs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/lambdalabs/api.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/lambdalabs/auth.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/lambdalabs/gpus.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/lambdalabs/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/lambdalabs/preflight.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/lambdalabs/pricing.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/preflight.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/realized.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/runpod/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/runpod/api.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/runpod/auth.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/runpod/cost.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/runpod/gpus.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/runpod/jobs.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/runpod/keys.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/runpod/preflight.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/runpod/preload.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/runpod/pricing.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/runpod/slots.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/runpod/train/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/runpod/train/endpoints.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/vast/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/vast/api.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/vast/auth.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/vast/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/vast/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/vast/preflight.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/providers/vast/pricing.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/py.typed +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/runner/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/runner/deploy.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/runner/lifecycle.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/runner/verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/schema/fields.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/serve/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/serve/deploy.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/serve/pricing.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/serve/urls.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/__main__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/_locks.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/_runtime.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/app.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/auth.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/billing.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/db.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/environment_registry.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/envs.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/reconcile.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/routes/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/routes/envs.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/routes/meta.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/routes/serving.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/server/run_registry.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/flash/spec.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/infisical-entrypoint.sh +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/scripts/build_dev_dist.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/_helpers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/_helpers/runner.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/_helpers/specs.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/_helpers/vast.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/conftest.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/fixtures/math_eval.jsonl +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/fixtures/math_train.jsonl +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/live/__init__.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/live/conftest.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/live/test_lambda_live.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/live/test_runpod_live.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/live/test_vast_live.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_accounting_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_agent_flash_cli_contract.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_algorithms.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_allocator.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_b200_rollout_opt.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_backend_jobspec_contract.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_base_model_provenance.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_cancel_remote.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_chalk_kernels.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_charge_pricing.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_cli_commands_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_cli_errors.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_cli_help.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_cli_main_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_cli_managed.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_cli_render_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_cli_render_theme.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_client_server_integration.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_config_overrides.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_controlled_experiment_repairs.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_cost_analytical.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_cost_equation.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_cost_estimate.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_cost_hardware.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_cost_models.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_cost_rewards.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_dev_channel.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_disk_gb.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_endpoint_name.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_env_cache_evict.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_env_delete.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_env_download.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_env_download_api.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_env_no_install.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_env_publish.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_env_pull.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_env_pull_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_env_pull_loader_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_env_pull_managed_control_plane.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_env_push.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_env_rate_limit_resolve.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_flash_mvp.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_flash_worker.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_flashinfer_cache_dirs.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_github_urlopen_retry.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_gpus.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_grpo_mask_aware.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_grpo_params.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_grpo_sleep_gate.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_health_liveness.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_idle_endpoint_reaper.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_instance_bootstrap_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_kv_util.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_lambda_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_lambda_runner.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_logging.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_login_perms.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_lora_rank_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_lora_rank_preflight.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_managed_hf_repo.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_metrics_schema_agent_contract.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_mig_guard.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_multiturn_rollout.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_multiturn_rollout_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_multiturn_rollout_records.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_multiturn_rollout_request_policy.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_oom_escalate_gpu.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_opd_full_state_resume.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_opd_resume_safety.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_open_model_policy.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_orchestrator_flash.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_packing.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_packing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_poll_helpers.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_preflight.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_provider_preflight_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_provider_routing.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_provider_teardown_robustness.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_providers_symmetry.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_realized_cost.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_resolve_params_b.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_resume_on_retry.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_runmgmt.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_runpod_api_delete.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_runpod_key_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_runpod_key_waterfall.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_runpod_slots.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_serve.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_server_app_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_server_billing.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_server_checkpoints_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_server_db.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_server_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_server_locks_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_serving_contract.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_serving_pricing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_serving_schema_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_sft_example_selection.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_sft_gc_off.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_sft_max_context.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_spec_and_validation.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_thinking_config.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_training_controls.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_update_check.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_vast_api.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_vast_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_vast_offers.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_vast_provider_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_vast_runner.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_verifiers.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_version.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_vl_warmstart_adapter_keys.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_wandb_log_coverage.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_wandb_naming.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_weight_cache.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_worker_dryrun.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_worker_hardexit.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_worker_image.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_worker_init_heartbeat.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/tests/test_worker_stack.py +0 -0
- {freesolo_flash_dev-1.0.0 → freesolo_flash_dev-1.0.1}/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: 1.0.
|
|
3
|
+
Version: 1.0.1
|
|
4
4
|
Summary: Flash — managed LoRA post-training (SFT/GRPO/OPD) for Freesolo environments, driven by the `flash` CLI
|
|
5
5
|
Project-URL: Homepage, https://github.com/freesolo-co/flash
|
|
6
6
|
Project-URL: Repository, https://github.com/freesolo-co/flash
|
|
@@ -111,6 +111,8 @@ class ModelInfo:
|
|
|
111
111
|
# multimodal-nested config, so the curated values are what actually engage the gate.
|
|
112
112
|
num_layers: int = 0
|
|
113
113
|
hidden_size: int = 0
|
|
114
|
+
# vllm hybrid-mamba cache block size in tokens. 0 means the model has no catalogued mamba floor.
|
|
115
|
+
mamba_block_size: int = 0
|
|
114
116
|
|
|
115
117
|
@property
|
|
116
118
|
def is_moe(self) -> bool:
|
|
@@ -124,6 +126,8 @@ class ModelInfo:
|
|
|
124
126
|
|
|
125
127
|
def to_dict(self) -> dict[str, Any]:
|
|
126
128
|
data = asdict(self)
|
|
129
|
+
if not data["mamba_block_size"]:
|
|
130
|
+
del data["mamba_block_size"]
|
|
127
131
|
serving = data["serving"]
|
|
128
132
|
if serving is None:
|
|
129
133
|
del data["serving"]
|
|
@@ -170,7 +174,7 @@ MODELS: dict[str, ModelInfo] = {
|
|
|
170
174
|
serve_model_id=SERVING_FP8_MODEL_REPOS["openbmb/MiniCPM5-1B"],
|
|
171
175
|
max_loras=16,
|
|
172
176
|
max_lora_rank=128,
|
|
173
|
-
max_model_len=
|
|
177
|
+
max_model_len=32768,
|
|
174
178
|
),
|
|
175
179
|
thinking="hybrid",
|
|
176
180
|
notes="On-device class SLM (131k ctx); standard Llama architecture.",
|
|
@@ -189,7 +193,7 @@ MODELS: dict[str, ModelInfo] = {
|
|
|
189
193
|
serve_model_id=SERVING_FP8_MODEL_REPOS["Qwen/Qwen3.5-0.8B"],
|
|
190
194
|
max_loras=16,
|
|
191
195
|
max_lora_rank=128,
|
|
192
|
-
max_model_len=
|
|
196
|
+
max_model_len=32768,
|
|
193
197
|
),
|
|
194
198
|
thinking="hybrid",
|
|
195
199
|
notes="Smallest Qwen3.5; cheap smoke/dev runs with the modern arch.",
|
|
@@ -208,7 +212,7 @@ MODELS: dict[str, ModelInfo] = {
|
|
|
208
212
|
serve_model_id=SERVING_FP8_MODEL_REPOS["Qwen/Qwen3.5-2B"],
|
|
209
213
|
max_loras=16,
|
|
210
214
|
max_lora_rank=128,
|
|
211
|
-
max_model_len=
|
|
215
|
+
max_model_len=32768,
|
|
212
216
|
),
|
|
213
217
|
thinking="hybrid",
|
|
214
218
|
),
|
|
@@ -226,7 +230,7 @@ MODELS: dict[str, ModelInfo] = {
|
|
|
226
230
|
serve_model_id=SERVING_FP8_MODEL_REPOS["Qwen/Qwen3.5-4B"],
|
|
227
231
|
max_loras=16,
|
|
228
232
|
max_lora_rank=64,
|
|
229
|
-
max_model_len=
|
|
233
|
+
max_model_len=32768,
|
|
230
234
|
max_num_seqs=8,
|
|
231
235
|
gpu_memory_utilization=0.98,
|
|
232
236
|
),
|
|
@@ -247,11 +251,11 @@ MODELS: dict[str, ModelInfo] = {
|
|
|
247
251
|
quant="bf16",
|
|
248
252
|
recommended_gpu="A100 PCIe",
|
|
249
253
|
serving=ServingCapacity(
|
|
250
|
-
gpu="
|
|
254
|
+
gpu="H100",
|
|
251
255
|
serve_model_id=SERVING_FP8_MODEL_REPOS["Qwen/Qwen3.5-9B"],
|
|
252
256
|
max_loras=16,
|
|
253
257
|
max_lora_rank=64,
|
|
254
|
-
max_model_len=
|
|
258
|
+
max_model_len=32768,
|
|
255
259
|
max_num_seqs=8,
|
|
256
260
|
gpu_memory_utilization=0.98,
|
|
257
261
|
),
|
|
@@ -271,6 +275,10 @@ MODELS: dict[str, ModelInfo] = {
|
|
|
271
275
|
# layers x 2048 hidden (hybrid GatedDeltaNet + full-attention, 256 experts / 8 active).
|
|
272
276
|
num_layers=40,
|
|
273
277
|
hidden_size=2048,
|
|
278
|
+
# vllm 0.19.1 model_executor/models/config.py derives this from the qwen config via
|
|
279
|
+
# mamba_utils.py: the 1,097,728-byte gdn state page needs 1072 fp8 attention tokens
|
|
280
|
+
# after the 16-token backend alignment applied by hybridattentionmambamodelconfig.
|
|
281
|
+
mamba_block_size=1072,
|
|
274
282
|
vocab_size=248_320,
|
|
275
283
|
algos=ALGORITHMS,
|
|
276
284
|
min_vram_gb=141,
|
|
@@ -312,6 +320,17 @@ MODELS: dict[str, ModelInfo] = {
|
|
|
312
320
|
}
|
|
313
321
|
|
|
314
322
|
|
|
323
|
+
def opd_mamba_batched_token_floor(
|
|
324
|
+
model_id: str, seq_cap: int, max_num_seqs: int | None
|
|
325
|
+
) -> int | None:
|
|
326
|
+
"""return a mamba floor only when vllm's derived opd scheduler budget is too small."""
|
|
327
|
+
info = MODELS.get(model_id)
|
|
328
|
+
if info is None or info.mamba_block_size <= 0 or max_num_seqs is None:
|
|
329
|
+
return None
|
|
330
|
+
derived_budget = int(max_num_seqs) * int(seq_cap)
|
|
331
|
+
return info.mamba_block_size if derived_budget < info.mamba_block_size else None
|
|
332
|
+
|
|
333
|
+
|
|
315
334
|
def list_models() -> list[ModelInfo]:
|
|
316
335
|
return sorted(MODELS.values(), key=lambda m: (m.min_vram_gb, m.id))
|
|
317
336
|
|
|
@@ -214,6 +214,7 @@ def _cmd_train_cost(args) -> int:
|
|
|
214
214
|
Catalog-only and deterministic. SFT cost never imports the environment; it requires a positive
|
|
215
215
|
[train].max_examples row count instead of guessing or locally counting a dataset."""
|
|
216
216
|
from flash.cost import estimate_cost
|
|
217
|
+
from flash.lora_rank import preflight_train_context_within_serving
|
|
217
218
|
|
|
218
219
|
spec = spec_from_file(
|
|
219
220
|
args.config,
|
|
@@ -221,6 +222,7 @@ def _cmd_train_cost(args) -> int:
|
|
|
221
222
|
overrides=args.overrides,
|
|
222
223
|
extra_configs=args.extra_configs,
|
|
223
224
|
)
|
|
225
|
+
preflight_train_context_within_serving(spec)
|
|
224
226
|
if spec.train.init_from_adapter:
|
|
225
227
|
# --cost is offline/catalog-only and cannot read the source adapter, so the rank stays at the
|
|
226
228
|
# local default. Warm starts train and are priced at the SOURCE adapter's authoritative rank
|
|
@@ -302,23 +304,24 @@ def cmd_train(args) -> int:
|
|
|
302
304
|
)
|
|
303
305
|
payload = spec_payload(spec, authored_train_keys=authored_train_keys)
|
|
304
306
|
client = client_from_config()
|
|
307
|
+
client_train_schema = {
|
|
308
|
+
"version": __version__,
|
|
309
|
+
"fields": train_schema_metadata(),
|
|
310
|
+
"authored_keys": sorted(authored_train_keys),
|
|
311
|
+
}
|
|
312
|
+
runtime_secrets = (
|
|
313
|
+
runtime_secrets_from_local_env(args.config, keys=spec.environment.secrets) or None
|
|
314
|
+
)
|
|
305
315
|
if args.dry_run:
|
|
306
|
-
#
|
|
307
|
-
#
|
|
308
|
-
#
|
|
309
|
-
# allocates no GPU and charges nothing. Local runtime secrets aren't sent — the server
|
|
310
|
-
# relaxes the required-[environment].secrets check for a preview, so `--dry-run` works before
|
|
311
|
-
# prod secrets are wired up. A rejection surfaces as the server's `error: ...` (exit 1),
|
|
312
|
-
# exactly as a real submit would.
|
|
316
|
+
# dry-run is a faithful server-side preview: it sends the same declared secrets and runs the
|
|
317
|
+
# same config, warm-start, serving, and cost preflights as a real submit, but allocates no gpu
|
|
318
|
+
# and charges nothing. a rejection surfaces as the server's error with exit status 1.
|
|
313
319
|
try:
|
|
314
320
|
status = client.create_run(
|
|
315
321
|
payload,
|
|
322
|
+
runtime_secrets=runtime_secrets,
|
|
316
323
|
dry_run=True,
|
|
317
|
-
client_train_schema=
|
|
318
|
-
"version": __version__,
|
|
319
|
-
"fields": train_schema_metadata(),
|
|
320
|
-
"authored_keys": sorted(authored_train_keys),
|
|
321
|
-
},
|
|
324
|
+
client_train_schema=client_train_schema,
|
|
322
325
|
)
|
|
323
326
|
except ApiError as exc:
|
|
324
327
|
detail = _legacy_train_key_rejection_detail(exc, authored_train_keys)
|
|
@@ -338,7 +341,8 @@ def cmd_train(args) -> int:
|
|
|
338
341
|
return 0
|
|
339
342
|
status = client.create_run(
|
|
340
343
|
payload,
|
|
341
|
-
runtime_secrets=
|
|
344
|
+
runtime_secrets=runtime_secrets,
|
|
345
|
+
client_train_schema=client_train_schema,
|
|
342
346
|
)
|
|
343
347
|
run_id = status["run_id"]
|
|
344
348
|
logger.info(
|
|
@@ -343,8 +343,8 @@ class ApiClient:
|
|
|
343
343
|
# Server-side preview: runs the same validation/preflights as a real submit and records a
|
|
344
344
|
# state=dry_run run, but allocates no GPU and charges nothing. Returns that status.
|
|
345
345
|
body["dry_run"] = True
|
|
346
|
-
|
|
347
|
-
|
|
346
|
+
if client_train_schema is not None:
|
|
347
|
+
body["client_train_schema"] = client_train_schema
|
|
348
348
|
return self._request("POST", "/v1/runs", body=body)
|
|
349
349
|
|
|
350
350
|
def list_runs(self) -> list[dict]:
|
|
@@ -127,7 +127,8 @@ def opd_vllm_kwargs(model_id: str, knobs: Any, seq_cap: int) -> dict[str, Any]:
|
|
|
127
127
|
|
|
128
128
|
fp8_kv = bool(cc >= (8, 9))
|
|
129
129
|
kwargs["kv_cache_dtype"] = "fp8" if fp8_kv else None
|
|
130
|
-
|
|
130
|
+
if card_gb >= 140:
|
|
131
|
+
kwargs["max_num_batched_tokens"] = max(8192, int(seq_cap))
|
|
131
132
|
|
|
132
133
|
if card_gb > 0:
|
|
133
134
|
try:
|
|
@@ -189,6 +190,12 @@ def opd_vllm_kwargs(model_id: str, knobs: Any, seq_cap: int) -> dict[str, Any]:
|
|
|
189
190
|
kwargs["rollout_batch_size"] = rollout_concurrency
|
|
190
191
|
except Exception as exc:
|
|
191
192
|
print(f"[opd] vLLM memory-util sizing failed; using 0.10: {exc}")
|
|
193
|
+
if card_gb < 140:
|
|
194
|
+
from flash.catalog import opd_mamba_batched_token_floor
|
|
195
|
+
|
|
196
|
+
kwargs["max_num_batched_tokens"] = opd_mamba_batched_token_floor(
|
|
197
|
+
model_id, seq_cap, kwargs["max_num_seqs"]
|
|
198
|
+
)
|
|
192
199
|
if startup_oom is not None:
|
|
193
200
|
raise startup_oom
|
|
194
201
|
|
|
@@ -691,7 +691,7 @@ def _prepare_init_from_adapter(
|
|
|
691
691
|
owner_key_id: int | None = None,
|
|
692
692
|
token: str | None = None,
|
|
693
693
|
) -> tuple[JobSpec, JobSpec, dict | None]:
|
|
694
|
-
"""
|
|
694
|
+
"""prepare public and worker specs with source-authoritative adapter metadata."""
|
|
695
695
|
_require_supported_adapter_continuation(spec)
|
|
696
696
|
ref = spec.train.init_from_adapter
|
|
697
697
|
if not ref:
|
|
@@ -776,7 +776,7 @@ def _prepare_init_from_adapter(
|
|
|
776
776
|
)
|
|
777
777
|
assert metadata is not None
|
|
778
778
|
identity = adapter_artifact_identity(storage, config, token, revision).to_dict()
|
|
779
|
-
public_spec = spec
|
|
779
|
+
public_spec = replace(spec, train=replace(spec.train, lora_alpha=metadata.alpha))
|
|
780
780
|
worker_spec = replace(
|
|
781
781
|
worker_spec,
|
|
782
782
|
train=replace(worker_spec.train, lora_rank=metadata.rank, lora_alpha=metadata.alpha),
|
|
@@ -546,7 +546,12 @@ def _validate_grpo(spec: JobSpec) -> None:
|
|
|
546
546
|
|
|
547
547
|
|
|
548
548
|
def _validate_opd(spec: JobSpec) -> None:
|
|
549
|
-
"""validate
|
|
549
|
+
"""validate opd-specific training constraints."""
|
|
550
|
+
if spec.train.kl_penalty_coef == 0.0:
|
|
551
|
+
raise ConfigError(
|
|
552
|
+
"[train] kl_penalty_coef must be > 0 for opd because it scales the gkd "
|
|
553
|
+
"distillation objective; omit it to use the default 1.0 or set a positive value"
|
|
554
|
+
)
|
|
550
555
|
if spec.train.max_context_tokens:
|
|
551
556
|
# Mirror run_opd's prompt-budget guard at PARSE time: a context budget that leaves no room
|
|
552
557
|
# for any prompt after the completion budget is rejected here, BEFORE a paid worker is
|
|
@@ -564,6 +569,9 @@ def _validate_opd(spec: JobSpec) -> None:
|
|
|
564
569
|
f"invalid budget fails before a GPU worker is provisioned."
|
|
565
570
|
)
|
|
566
571
|
|
|
572
|
+
# short hybrid-mamba contexts are safe because the worker compares vllm's derived scheduler
|
|
573
|
+
# budget with the catalogued block size and supplies an explicit floor only when required.
|
|
574
|
+
|
|
567
575
|
|
|
568
576
|
# Each algorithm's spec-level contract lives in ONE validator, dispatched by name so a new algorithm
|
|
569
577
|
# adds a function + a map entry rather than another scattered ``if spec.algorithm == ...`` block.
|
|
@@ -6,9 +6,12 @@ the destination model repo, so the user never needs access to internal artifact
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
+
import contextlib
|
|
9
10
|
import json
|
|
10
11
|
import os
|
|
11
12
|
import re
|
|
13
|
+
import shutil
|
|
14
|
+
import struct
|
|
12
15
|
import tempfile
|
|
13
16
|
from pathlib import Path
|
|
14
17
|
|
|
@@ -23,6 +26,128 @@ _STALE_ADAPTER_DELETE_PATTERNS = ["adapter_model*", "adapter_config.json"]
|
|
|
23
26
|
_TEMP_MERGED_BASE_MODEL_RE = re.compile(
|
|
24
27
|
r"(?:/[^\s\"'`,\]\){}]+)*/flash_sft_merged_[^\s\"'`,\]\){}]+"
|
|
25
28
|
)
|
|
29
|
+
_MAX_SAFETENSORS_HEADER_BYTES = 100 * 1024 * 1024
|
|
30
|
+
|
|
31
|
+
# module-path segments outside the language model; mirrors _VL_EXCLUDE_SEGMENTS in
|
|
32
|
+
# flash/engine/worker/lora.py. today's managed adapters never touch these (the worker
|
|
33
|
+
# excludes them from lora), so their presence means a genuinely multimodal adapter whose
|
|
34
|
+
# vision keys the text-only strip cannot represent. normalizing only the lm keys of such
|
|
35
|
+
# an adapter would produce a mixed namespace that matches no transformers class, so the
|
|
36
|
+
# normalization must skip the file entirely and export it as trained.
|
|
37
|
+
_NON_LM_KEY_SEGMENTS = (".visual.", ".vision_tower.", ".multi_modal_projector.", ".mtp.")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _references_non_lm_modules(key: str) -> bool:
|
|
41
|
+
return any(segment in key for segment in _NON_LM_KEY_SEGMENTS)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _strip_language_model_infix(key: str) -> str:
|
|
45
|
+
# mirrors _LANGUAGE_MODEL_INFIX namespace semantics in flash/engine/worker/lora.py
|
|
46
|
+
infix = ".language_model."
|
|
47
|
+
index = key.find(infix)
|
|
48
|
+
if index == -1:
|
|
49
|
+
return key
|
|
50
|
+
return key[:index] + "." + key[index + len(infix) :]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _json_object_without_duplicate_keys(pairs: list[tuple[str, object]]) -> dict[str, object]:
|
|
54
|
+
value: dict[str, object] = {}
|
|
55
|
+
for key, item in pairs:
|
|
56
|
+
if key in value:
|
|
57
|
+
raise ValueError(f"duplicate safetensors JSON key {key!r}")
|
|
58
|
+
value[key] = item
|
|
59
|
+
return value
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _normalize_export_adapter_keys(adapter_dir: Path) -> bool:
|
|
63
|
+
path = adapter_dir / "adapter_model.safetensors"
|
|
64
|
+
if not path.is_file():
|
|
65
|
+
logger.warning("exported adapter has no safetensors weights; leaving weights unchanged")
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
temp_path: Path | None = None
|
|
69
|
+
try:
|
|
70
|
+
file_size = path.stat().st_size
|
|
71
|
+
with path.open("rb") as source:
|
|
72
|
+
length_bytes = source.read(8)
|
|
73
|
+
if len(length_bytes) != 8:
|
|
74
|
+
raise ValueError("file is too small to contain a safetensors header")
|
|
75
|
+
(header_length,) = struct.unpack("<Q", length_bytes)
|
|
76
|
+
if (
|
|
77
|
+
header_length > file_size - 8
|
|
78
|
+
or header_length > _MAX_SAFETENSORS_HEADER_BYTES
|
|
79
|
+
):
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"declared header length {header_length} is implausible for {file_size}-byte file"
|
|
82
|
+
)
|
|
83
|
+
header_bytes = source.read(header_length)
|
|
84
|
+
if len(header_bytes) != header_length:
|
|
85
|
+
raise ValueError("safetensors header is truncated")
|
|
86
|
+
header = json.loads(
|
|
87
|
+
header_bytes,
|
|
88
|
+
object_pairs_hook=_json_object_without_duplicate_keys,
|
|
89
|
+
)
|
|
90
|
+
if not isinstance(header, dict):
|
|
91
|
+
raise ValueError("safetensors header is not a JSON object")
|
|
92
|
+
|
|
93
|
+
non_lm = [
|
|
94
|
+
key
|
|
95
|
+
for key in header
|
|
96
|
+
if key != "__metadata__" and _references_non_lm_modules(key)
|
|
97
|
+
]
|
|
98
|
+
if non_lm:
|
|
99
|
+
logger.info(
|
|
100
|
+
"exported adapter touches non-lm modules (e.g. %s); keeping the "
|
|
101
|
+
"multimodal key namespace unchanged",
|
|
102
|
+
non_lm[0],
|
|
103
|
+
)
|
|
104
|
+
return False
|
|
105
|
+
|
|
106
|
+
normalized: dict[str, object] = {}
|
|
107
|
+
remapped = 0
|
|
108
|
+
for key, value in header.items():
|
|
109
|
+
normalized_key = (
|
|
110
|
+
key if key == "__metadata__" else _strip_language_model_infix(key)
|
|
111
|
+
)
|
|
112
|
+
if normalized_key in normalized:
|
|
113
|
+
raise ValueError(
|
|
114
|
+
f"key {key!r} collides after stripping the '.language_model.' infix"
|
|
115
|
+
)
|
|
116
|
+
normalized[normalized_key] = value
|
|
117
|
+
remapped += int(normalized_key != key)
|
|
118
|
+
if remapped == 0:
|
|
119
|
+
return False
|
|
120
|
+
|
|
121
|
+
new_header = json.dumps(
|
|
122
|
+
normalized,
|
|
123
|
+
ensure_ascii=False,
|
|
124
|
+
separators=(",", ":"),
|
|
125
|
+
).encode("utf-8")
|
|
126
|
+
new_header += b" " * (-len(new_header) % 8)
|
|
127
|
+
with tempfile.NamedTemporaryFile(
|
|
128
|
+
mode="wb",
|
|
129
|
+
dir=path.parent,
|
|
130
|
+
prefix=f".{path.name}.",
|
|
131
|
+
delete=False,
|
|
132
|
+
) as destination:
|
|
133
|
+
temp_path = Path(destination.name)
|
|
134
|
+
destination.write(struct.pack("<Q", len(new_header)))
|
|
135
|
+
destination.write(new_header)
|
|
136
|
+
shutil.copyfileobj(source, destination)
|
|
137
|
+
destination.flush()
|
|
138
|
+
os.fsync(destination.fileno())
|
|
139
|
+
os.replace(temp_path, path)
|
|
140
|
+
temp_path = None
|
|
141
|
+
except Exception as exc:
|
|
142
|
+
logger.warning("could not normalize exported adapter keys; leaving weights unchanged: %s", exc)
|
|
143
|
+
return False
|
|
144
|
+
finally:
|
|
145
|
+
if temp_path is not None:
|
|
146
|
+
with contextlib.suppress(OSError):
|
|
147
|
+
temp_path.unlink(missing_ok=True)
|
|
148
|
+
|
|
149
|
+
logger.info("normalized %d exported adapter weight keys", remapped)
|
|
150
|
+
return True
|
|
26
151
|
|
|
27
152
|
|
|
28
153
|
def _rewrite_adapter_config_base_model(
|
|
@@ -94,6 +219,23 @@ def _hf_api():
|
|
|
94
219
|
return HfApi, snapshot_download
|
|
95
220
|
|
|
96
221
|
|
|
222
|
+
def _hub_error_types() -> tuple[type[BaseException], ...]:
|
|
223
|
+
error_types: list[type[BaseException]] = []
|
|
224
|
+
try:
|
|
225
|
+
from huggingface_hub.errors import HfHubHTTPError
|
|
226
|
+
except ImportError:
|
|
227
|
+
pass
|
|
228
|
+
else:
|
|
229
|
+
error_types.append(HfHubHTTPError)
|
|
230
|
+
try:
|
|
231
|
+
from requests import RequestException
|
|
232
|
+
except ImportError:
|
|
233
|
+
pass
|
|
234
|
+
else:
|
|
235
|
+
error_types.append(RequestException)
|
|
236
|
+
return tuple(error_types)
|
|
237
|
+
|
|
238
|
+
|
|
97
239
|
def export_adapter(
|
|
98
240
|
*,
|
|
99
241
|
source_repo: str,
|
|
@@ -125,9 +267,9 @@ def export_adapter(
|
|
|
125
267
|
local_dir=tmp,
|
|
126
268
|
token=read_token,
|
|
127
269
|
)
|
|
128
|
-
except OSError:
|
|
129
|
-
raise
|
|
130
270
|
except Exception as exc:
|
|
271
|
+
if isinstance(exc, OSError) and not isinstance(exc, _hub_error_types()):
|
|
272
|
+
raise
|
|
131
273
|
raise ServingError(
|
|
132
274
|
f"could not download adapter {source_repo}:{source_subfolder}: {exc}"
|
|
133
275
|
) from exc
|
|
@@ -143,6 +285,7 @@ def export_adapter(
|
|
|
143
285
|
"(need adapter_config.json + an adapter_model* weight; nothing to export)"
|
|
144
286
|
)
|
|
145
287
|
_repair_export_metadata(adapter_dir, base_model, base_model_revision)
|
|
288
|
+
_normalize_export_adapter_keys(adapter_dir)
|
|
146
289
|
api = HfApi(token=dest_token)
|
|
147
290
|
try:
|
|
148
291
|
# Always create private first so the repo is never transiently exposed empty/partial.
|
|
@@ -161,9 +304,9 @@ def export_adapter(
|
|
|
161
304
|
)
|
|
162
305
|
if not private:
|
|
163
306
|
api.update_repo_settings(repo_id=dest_repo, repo_type="model", private=False)
|
|
164
|
-
except OSError:
|
|
165
|
-
raise
|
|
166
307
|
except Exception as exc:
|
|
308
|
+
if isinstance(exc, OSError) and not isinstance(exc, _hub_error_types()):
|
|
309
|
+
raise
|
|
167
310
|
raise ServingError(f"could not upload adapter to {dest_repo}: {exc}") from exc
|
|
168
311
|
logger.info(
|
|
169
312
|
"exported %s:%s -> %s (%d files)", source_repo, source_subfolder, dest_repo, len(files)
|
|
@@ -74,9 +74,7 @@ def _parse_spec(payload: dict, run_id: str) -> JobSpec:
|
|
|
74
74
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
|
75
75
|
|
|
76
76
|
|
|
77
|
-
def _runtime_secrets(
|
|
78
|
-
payload: dict, spec: JobSpec, *, require_environment_secrets: bool
|
|
79
|
-
) -> dict[str, str]:
|
|
77
|
+
def _runtime_secrets(payload: dict, spec: JobSpec) -> dict[str, str]:
|
|
80
78
|
# Use get()+None check, not `or {}` — falsy non-objects must still hit the type check below.
|
|
81
79
|
raw = payload.get("runtime_secrets")
|
|
82
80
|
if raw is None:
|
|
@@ -102,14 +100,12 @@ def _runtime_secrets(
|
|
|
102
100
|
value = value.strip()
|
|
103
101
|
if value:
|
|
104
102
|
out[key] = value
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
),
|
|
114
|
-
)
|
|
103
|
+
missing = sorted(set(spec.environment.secrets) - set(out))
|
|
104
|
+
if missing:
|
|
105
|
+
raise HTTPException(
|
|
106
|
+
status_code=400,
|
|
107
|
+
detail=(
|
|
108
|
+
f"missing runtime secret(s) required by [environment] secrets: {', '.join(missing)}"
|
|
109
|
+
),
|
|
110
|
+
)
|
|
115
111
|
return out
|
|
@@ -59,6 +59,7 @@ plane with an operator ``HF_TOKEN`` — without it the sweep cannot delete opera
|
|
|
59
59
|
from __future__ import annotations
|
|
60
60
|
|
|
61
61
|
import os
|
|
62
|
+
import threading
|
|
62
63
|
import time
|
|
63
64
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
64
65
|
from dataclasses import dataclass
|
|
@@ -87,6 +88,26 @@ RUN_REPO_PREFIX = "flashrun-"
|
|
|
87
88
|
DELETE_AGE_SECONDS = 7.0 * 86400.0
|
|
88
89
|
_DELETE_SLEEP_S = 0.5 # pause between deletes — HF repo-mutation rate-limit courtesy
|
|
89
90
|
_SCAN_WORKERS = 8 # hf tree-listing concurrency
|
|
91
|
+
# ``list_repo_tree(..., expand=True)`` is HF's most expensive listing mode (expand bypasses the
|
|
92
|
+
# CDN and hits origin for a last_commit lookup per entry) -- it rate-limits well below the
|
|
93
|
+
# general API quota. _scan_repo (one call per repo, fanned across _SCAN_WORKERS) and
|
|
94
|
+
# _prefix_written_within (one call per delete target) both hit it; with no shared pacing, the
|
|
95
|
+
# worker pool lets up to _SCAN_WORKERS calls land on HF in the same instant, which is enough on
|
|
96
|
+
# its own to trip the rate limit on a sweep over more than a handful of repos. This floor paces
|
|
97
|
+
# EVERY list_repo_tree call across the whole sweep (not per-thread), so concurrency no longer
|
|
98
|
+
# translates into a request burst.
|
|
99
|
+
_TREE_LIST_MIN_INTERVAL_S = 0.5 # cap ~2 req/s across the whole sweep, workers included
|
|
100
|
+
_tree_list_lock = threading.Lock()
|
|
101
|
+
_tree_list_last_call = 0.0
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _throttle_tree_list() -> None:
|
|
105
|
+
global _tree_list_last_call
|
|
106
|
+
with _tree_list_lock:
|
|
107
|
+
wait = _tree_list_last_call + _TREE_LIST_MIN_INTERVAL_S - time.monotonic()
|
|
108
|
+
if wait > 0:
|
|
109
|
+
time.sleep(wait)
|
|
110
|
+
_tree_list_last_call = time.monotonic()
|
|
90
111
|
|
|
91
112
|
# Only these top-level dirs are deletable model-artifact phases (``JobSpec.phase``: grpo -> "rl",
|
|
92
113
|
# plus the VL warm-start ``recomb`` recombined adapter). Anything else — ``code/`` snapshots,
|
|
@@ -187,6 +208,7 @@ def _scan_repo(api, repo_id: str) -> tuple[dict[str, list], float | None, set[st
|
|
|
187
208
|
``prefixes`` maps ``"<phase>/<run_id>"`` -> ``[total_size, newest_commit_ts]`` for ARTIFACT_PHASES
|
|
188
209
|
only, ``ref_recent_ts`` is the newest commit among ``referenced_by/`` markers (warm-start-source
|
|
189
210
|
signal), and ``unknown_tops`` are unrecognized top-level dirs (reported, never deleted)."""
|
|
211
|
+
_throttle_tree_list()
|
|
190
212
|
entries = api.list_repo_tree(
|
|
191
213
|
repo_id=repo_id, repo_type="dataset", recursive=True, expand=True
|
|
192
214
|
)
|
|
@@ -246,6 +268,7 @@ def _prefix_written_within(api, repo_id: str, prefix: str, now: float, max_age_s
|
|
|
246
268
|
redeploy read-back that landed after enumeration must spare the prefix. Raises on an API error (the
|
|
247
269
|
caller then skips this target to stay safe). If the listing carries no commit dates (older
|
|
248
270
|
``huggingface_hub``), returns ``False`` — the enumeration age gate already qualified it."""
|
|
271
|
+
_throttle_tree_list()
|
|
249
272
|
entries = api.list_repo_tree(
|
|
250
273
|
repo_id=repo_id, repo_type="dataset", path_in_repo=prefix, recursive=True, expand=True
|
|
251
274
|
)
|
|
@@ -102,11 +102,11 @@ def _precheck_budget_or_block(*, run_id: str, estimate_usd: float, org_id: str)
|
|
|
102
102
|
def create_run(payload: dict, key: Annotated[dict, Depends(require_key)]):
|
|
103
103
|
dry_run = _require_bool(payload, "dry_run", False)
|
|
104
104
|
schema = _client_train_schema(payload)
|
|
105
|
+
submitted = payload.get("spec")
|
|
106
|
+
submitted_train = submitted.get("train") if isinstance(submitted, dict) else None
|
|
105
107
|
try:
|
|
106
108
|
spec = _parse_spec(payload, run_id=new_run_id())
|
|
107
109
|
except HTTPException as exc:
|
|
108
|
-
submitted = payload.get("spec")
|
|
109
|
-
submitted_train = submitted.get("train") if isinstance(submitted, dict) else None
|
|
110
110
|
server_fields = train_schema_metadata()
|
|
111
111
|
unsupported = (
|
|
112
112
|
sorted(
|
|
@@ -130,7 +130,7 @@ def create_run(payload: dict, key: Annotated[dict, Depends(require_key)]):
|
|
|
130
130
|
)
|
|
131
131
|
raise HTTPException(status_code=400, detail=detail) from exc
|
|
132
132
|
raise
|
|
133
|
-
runtime_secrets = _runtime_secrets(payload, spec
|
|
133
|
+
runtime_secrets = _runtime_secrets(payload, spec)
|
|
134
134
|
affordability_org_id = str(key.get("org_id") or "").strip()
|
|
135
135
|
bill_on_completion = not dry_run and key.get("auth_kind") != "internal"
|
|
136
136
|
billing_context = None
|
|
@@ -176,6 +176,21 @@ def create_run(payload: dict, key: Annotated[dict, Depends(require_key)]):
|
|
|
176
176
|
),
|
|
177
177
|
) from exc
|
|
178
178
|
raise
|
|
179
|
+
if (
|
|
180
|
+
spec.train.init_from_adapter
|
|
181
|
+
and schema is not None
|
|
182
|
+
and "lora_alpha" in schema["authored_keys"]
|
|
183
|
+
and spec.train.lora_alpha != prepared.worker_spec.train.lora_alpha
|
|
184
|
+
):
|
|
185
|
+
raise HTTPException(
|
|
186
|
+
status_code=400,
|
|
187
|
+
detail=(
|
|
188
|
+
f"train.lora_alpha={spec.train.lora_alpha} does not match the "
|
|
189
|
+
"train.init_from_adapter source adapter "
|
|
190
|
+
f"lora_alpha={prepared.worker_spec.train.lora_alpha}; omit train.lora_alpha "
|
|
191
|
+
"because source adapter alpha metadata is authoritative"
|
|
192
|
+
),
|
|
193
|
+
)
|
|
179
194
|
run_id = prepared.public_spec.run_id
|
|
180
195
|
if bill_on_completion:
|
|
181
196
|
_precheck_budget_or_block(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "freesolo-flash-dev"
|
|
3
|
-
version = "1.0.
|
|
3
|
+
version = "1.0.1"
|
|
4
4
|
description = "Flash — managed LoRA post-training (SFT/GRPO/OPD) for Freesolo environments, driven by the `flash` CLI"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
# RunPod Flash supports Python 3.11-3.12 (not 3.13 yet).
|
|
@@ -113,7 +113,7 @@ artifacts = ["flash/py.typed"]
|
|
|
113
113
|
# auto-published by .github/workflows/publish-dev.yml: a push to `dev` whose version isn't yet on
|
|
114
114
|
# PyPI publishes it; pushes that don't change it find it already published and no-op. So cutting a
|
|
115
115
|
# dev-channel release = bump this (and [project].version) and merge to `dev`.
|
|
116
|
-
version = "1.0.
|
|
116
|
+
version = "1.0.1"
|
|
117
117
|
|
|
118
118
|
[tool.pytest.ini_options]
|
|
119
119
|
pythonpath = ["."]
|