freesolo-flash-dev 0.2.48__tar.gz → 0.2.49__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.48 → freesolo_flash_dev-0.2.49}/PKG-INFO +1 -1
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/cli/commands.py +4 -2
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/cli/training_doc.py +14 -11
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/cost/spec.py +63 -13
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/recipe.py +2 -3
- freesolo_flash_dev-0.2.49/flash/engine/steps.py +18 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/opd.py +35 -7
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/rl.py +18 -4
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/_worker.py +0 -4
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/schema/__init__.py +2 -9
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/spec.py +0 -2
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/pyproject.toml +2 -2
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_agent_flash_cli_contract.py +2 -1
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_algorithms.py +4 -4
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_allocator.py +10 -10
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_backend_jobspec_contract.py +6 -4
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_billing_retry.py +1 -1
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_charge_pricing.py +1 -1
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_checkpoints.py +2 -2
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_cli_commands.py +4 -2
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_cli_errors.py +5 -3
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_cli_estimate.py +29 -13
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_cli_managed.py +2 -1
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_client_server_integration.py +1 -1
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_config_overrides.py +9 -5
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_env_publish.py +1 -1
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_env_rate_limit_resolve.py +1 -1
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_flash_mvp.py +4 -2
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_flash_worker.py +8 -8
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_gpus.py +3 -2
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_grpo_params.py +18 -17
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_jobs.py +19 -18
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_lora_rank_preflight.py +2 -3
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_opd.py +20 -18
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_orchestrator_flash.py +1 -1
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_runmgmt.py +2 -2
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_server_api.py +13 -13
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_server_billing.py +1 -1
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_spec_and_validation.py +2 -3
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_weight_cache.py +1 -1
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_worker_dryrun.py +13 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/.dockerignore +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/.env.example +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/.github/workflows/auto-rebake.yml +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/.github/workflows/bake-kernel-cache.yml +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/.github/workflows/ci.yml +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/.github/workflows/main-source-guard.yml +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/.github/workflows/notify-tests-repo.yml +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/.github/workflows/publish-dev.yml +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/.github/workflows/publish-image.yml +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/.github/workflows/publish.yml +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/.github/workflows/version-parity.yml +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/.github/workflows/worker-image.yml +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/.gitignore +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/Dockerfile +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/Dockerfile.worker +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/LICENSE +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/README.md +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/build/kernel_cache/.gitignore +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/build/kernel_cache/.keep +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/docker/Dockerfile.kernelcache +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/docker/Dockerfile.kernelcache.relayer +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/docker/bake_kernel_cache.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/docker/bake_pod_entry.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/docker/kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/docker/make_rp_handler.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/_channel.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/_fileio.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/_logging.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/_update_check.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/adapter_artifacts.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/catalog.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/cli/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/cli/__main__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/cli/_tty.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/cli/envpush.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/cli/render.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/client/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/client/config.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/client/http.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/client/runtime_secrets.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/client/specs.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/cost/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/cost/analytical.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/cost/facts.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/cost/types.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/accounting.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/chalk_kernels.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/multiturn_rollout.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/vram.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/__main__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/_pkg.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/adapter.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/decoding.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/finalize.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/gpu_setup.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/grpo.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/heartbeat.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/hf.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/kernel_warmup.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/lora.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/opd_gkd.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/opd_vllm.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/packing.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/attn.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/diagnostics.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/lifecycle.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/liger.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/loraplus.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/perf/memory.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/sft.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/teacher.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/tokenizer_align.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/engine/worker/wandb_log.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/envs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/envs/adapter.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/envs/archive_policy.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/envs/base.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/envs/loader.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/envs/pull.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/envs/registry.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/lora_rank.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/_auth.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/_hf_artifacts.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/_hf_retry.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/_http.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/_instance.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/_instance_bootstrap.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/_instance_poll.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/_instance_provider.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/_poll.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/allocator.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/base.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/api.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/auth.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/gpus.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/jobs/builders.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/preflight.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/lambdalabs/pricing.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/preflight.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/realized.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/api.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/auth.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/cost.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/gpus.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/jobs.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/keys.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/preflight.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/preload.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/pricing.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/slots.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/train/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/train/deps.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/runpod/train/endpoints.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/vast/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/vast/api.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/vast/auth.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/vast/jobs/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/vast/jobs/builders.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/vast/preflight.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/providers/vast/pricing.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/py.typed +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/runner/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/runner/checkpoints.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/runner/deploy.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/runner/lifecycle.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/schema/fields.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/serve/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/serve/deploy.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/serve/export.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/serve/pricing.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/__main__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/_deps.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/_internal_client.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/_locks.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/_runtime.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/app.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/auth.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/billing.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/billing_retry.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/checkpoints.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/db.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/environment_registry.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/envs.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/reconcile.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/routes/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/routes/envs.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/routes/meta.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/routes/runs.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/routes/serving.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/flash/server/run_registry.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/infisical-entrypoint.sh +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/scripts/build_dev_dist.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/_helpers/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/_helpers/runner.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/_helpers/specs.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/_helpers/vast.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/conftest.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/fixtures/math_eval.jsonl +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/fixtures/math_train.jsonl +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/live/__init__.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/live/conftest.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/live/test_lambda_live.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/live/test_runpod_live.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/live/test_vast_live.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_b200_rollout_opt.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_cancel_remote.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_catalog_consistency.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_chalk_kernels.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_cli_help.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_cli_render_theme.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_client.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_cost_analytical.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_cost_equation.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_cost_estimate.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_cost_hardware.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_cost_models.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_cost_rewards.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_dev_channel.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_disk_gb.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_endpoint_name.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_env_cache_evict.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_env_delete.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_env_download.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_env_download_api.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_env_no_install.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_env_pull.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_env_pull_managed_control_plane.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_env_push.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_envs_coverage.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_export.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_flashinfer_cache_dirs.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_github_urlopen_retry.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_grpo_mask_aware.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_grpo_sleep_gate.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_health_liveness.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_idle_endpoint_reaper.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_internal_client.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_kernel_cache.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_kv_util.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_lambda_runner.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_logging.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_login_perms.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_managed_hf_repo.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_metrics_schema_agent_contract.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_mig_guard.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_multiturn_rollout.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_oom_escalate_gpu.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_opd_vllm.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_open_model_policy.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_packing.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_poll_helpers.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_preflight.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_provider_routing.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_provider_teardown_robustness.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_providers_symmetry.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_realized_cost.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_resolve_params_b.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_resume_on_retry.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_runpod_api_delete.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_runpod_key_fingerprint.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_runpod_key_waterfall.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_runpod_slots.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_serve.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_server_db.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_serving_contract.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_sft_example_selection.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_sft_gc_off.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_thinking_config.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_train_context_preflight.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_update_check.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_vast_api.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_vast_offers.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_vast_runner.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_verifiers.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_version.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_vl_warmstart_adapter_keys.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_vl_warmstart_recombine.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_wandb_naming.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_warmstart_cross_repo.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_worker_hardexit.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_worker_image.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_worker_init_heartbeat.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_worker_stack.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_worker_thinking.py +0 -0
- {freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/uv.lock +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.49
|
|
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
|
|
@@ -469,7 +469,8 @@ def cmd_env_setup(args) -> int:
|
|
|
469
469
|
"\n"
|
|
470
470
|
f"{env_comment}"
|
|
471
471
|
"[train]\n"
|
|
472
|
-
"
|
|
472
|
+
"epochs = 1\n"
|
|
473
|
+
"max_examples = 2 # rows to train on; the starter dataset has 2 (raise as your dataset grows)\n"
|
|
473
474
|
f"{rl_reasoning_train}"
|
|
474
475
|
"lora_rank = 32\n"
|
|
475
476
|
"# GPU and HF artifacts are managed automatically by the platform: the GPU is\n"
|
|
@@ -511,7 +512,8 @@ def cmd_env_setup(args) -> int:
|
|
|
511
512
|
"[environment]\n"
|
|
512
513
|
'id = ""\n\n'
|
|
513
514
|
"[train]\n"
|
|
514
|
-
"
|
|
515
|
+
"epochs = 1\n"
|
|
516
|
+
"max_examples = 2 # rows to train on; the starter dataset has 2 (raise as your dataset grows)\n"
|
|
515
517
|
"lora_rank = 32\n"
|
|
516
518
|
"# GPU and HF artifacts are managed automatically by the platform: the GPU is\n"
|
|
517
519
|
"# the cheapest fitting managed class, and artifacts live in a private environment-scoped repo.\n"
|
|
@@ -103,7 +103,7 @@ id = "your-org/my-env" # the id printed by `flash env push`
|
|
|
103
103
|
# never stored in the spec
|
|
104
104
|
|
|
105
105
|
[train]
|
|
106
|
-
epochs = 1 #
|
|
106
|
+
epochs = 1 # one pass over the retained train rows
|
|
107
107
|
max_examples = 2 # rows to train on (the starter dataset has 2)
|
|
108
108
|
lora_rank = 32
|
|
109
109
|
lora_alpha = 64
|
|
@@ -114,7 +114,7 @@ GPU and HF artifacts are **fully managed** — do not pick `gpu.type` or set
|
|
|
114
114
|
`train.hf_repo`; the allocator picks the cheapest validated managed GPU class that fits,
|
|
115
115
|
and run artifacts are stored in a private environment-scoped repo with content-addressed
|
|
116
116
|
Flash code snapshots. Compose or tweak configs without editing files: `--config
|
|
117
|
-
extra.toml` (deep-merge) and `--set key=value` (e.g. `--set train.
|
|
117
|
+
extra.toml` (deep-merge) and `--set key=value` (e.g. `--set train.epochs=3`).
|
|
118
118
|
|
|
119
119
|
### 4. Submit
|
|
120
120
|
|
|
@@ -201,7 +201,7 @@ spending another GPU run:
|
|
|
201
201
|
| --- | --- | --- |
|
|
202
202
|
| Environment id is blank or stale | `flash train --dry-run` fails, or the worker uses old reward/data | Run `flash env push --name my-env .` after every environment/data edit and paste the returned id into every config you submit. |
|
|
203
203
|
| Local-only env path in config | Config validation says there is no local path mode | Publish first, then use the returned slug in `[environment] id`. `flash train` only runs published env ids, not local paths. |
|
|
204
|
-
| Config knobs are in the wrong table | Validation rejects `[grpo]`, `[sft]`, or unknown `[train]` keys | Put `
|
|
204
|
+
| Config knobs are in the wrong table | Validation rejects `[grpo]`, `[sft]`, or unknown `[train]` keys | Put `epochs`, `group_size`, `max_completion_tokens`, `temperature`, `max_context_tokens`, LoRA, and other training knobs under `[train]`. |
|
|
205
205
|
| Trying to pin managed infrastructure | `gpu.type`, `train.hf_repo`, or `model_policy` changes do not do what you expected | Treat GPU choice, model policy, and the run artifact repo as managed. Tune the model, algorithm, environment, and `[train]` knobs instead. |
|
|
206
206
|
| Secrets are not available on the worker | Reward code works locally but remote logs show missing API keys or auth failures | List secret names under `[environment] secrets = [...]`, export those env vars locally before submit, or put them in local `.env` / `.env.local`. Never put secret values in `[worker_env]` or hard-code them in the config. |
|
|
207
207
|
| Wrong model / thinking setting | Config validation fails, or chat behavior does not match the run | Use `flash models`; set `thinking = true` only for supported models. Thinking is a training-time/run-level choice and serving preserves that parity, so `flash chat` does not expose an override flag. |
|
|
@@ -214,7 +214,7 @@ spending another GPU run:
|
|
|
214
214
|
| 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. |
|
|
215
215
|
| 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`. |
|
|
216
216
|
| 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. |
|
|
217
|
-
| Cost surprises | A quick experiment uses more GPU time than intended | Start with `--dry-run` and `--cost`,
|
|
217
|
+
| Cost surprises | A quick experiment uses more GPU time than intended | Start with `--dry-run` and `--cost`, keep `epochs` and `max_examples` small 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. |
|
|
218
218
|
|
|
219
219
|
---
|
|
220
220
|
|
|
@@ -370,7 +370,9 @@ lora_rank = 16 # for VL warm-starts, SFT rank + GRPO rank must fit the effec
|
|
|
370
370
|
lora_alpha = 32
|
|
371
371
|
```
|
|
372
372
|
|
|
373
|
-
SFT
|
|
373
|
+
SFT, GRPO, and OPD all accept **epoch-driven** configs (`epochs`). For GRPO/OPD,
|
|
374
|
+
an epoch is one pass over the retained prompt pool after `max_examples` and prompt-budget filtering;
|
|
375
|
+
optimizer-step counts are derived from those epochs.
|
|
374
376
|
|
|
375
377
|
---
|
|
376
378
|
|
|
@@ -379,8 +381,8 @@ SFT is **epoch-driven** (`epochs`); GRPO is **step-driven** (`steps`).
|
|
|
379
381
|
Pick distillation when a much stronger **teacher** model can grade your student's work
|
|
380
382
|
token-by-token. The student samples on-policy (like GRPO), the managed GLM 5.2 teacher scores
|
|
381
383
|
each of *its own* completions, and a dense per-token loss teaches the student to match the teacher —
|
|
382
|
-
far more sample-efficient than reward-based RL and with no reward to design. It
|
|
383
|
-
|
|
384
|
+
far more sample-efficient than reward-based RL and with no reward to design. It supports `epochs`
|
|
385
|
+
like SFT/GRPO and produces a LoRA served exactly like SFT.
|
|
384
386
|
|
|
385
387
|
- **No teacher key or model override to set up.** The GLM 5.2 teacher and its Fireworks key are platform-managed: the
|
|
386
388
|
service supplies its own key to every opd run, so there is nothing to export or declare — an opd
|
|
@@ -412,7 +414,8 @@ algorithm = "opd"
|
|
|
412
414
|
id = "your-org/my-env"
|
|
413
415
|
|
|
414
416
|
[train]
|
|
415
|
-
|
|
417
|
+
epochs = 1
|
|
418
|
+
max_examples = 2
|
|
416
419
|
lora_rank = 32
|
|
417
420
|
# kl_penalty_coef = 1.0 # reverse-KL scale
|
|
418
421
|
```
|
|
@@ -449,7 +452,7 @@ rollouts score somewhere between all-fail and all-pass — is where GRPO has the
|
|
|
449
452
|
signal.
|
|
450
453
|
|
|
451
454
|
- If nearly every prompt is solved (most groups score ~1.0): **increase difficulty** —
|
|
452
|
-
harder prompts, tighter format/reward, more
|
|
455
|
+
harder prompts, tighter format/reward, more epochs, or more data.
|
|
453
456
|
- If nearly nothing is solved (most groups score ~0.0): **decrease difficulty** —
|
|
454
457
|
easier or few-shot prompts, a more lenient (denser) reward, or warm-start with SFT.
|
|
455
458
|
- In between: good signal — keep iterating at this difficulty.
|
|
@@ -500,8 +503,8 @@ on a beyond-noise improvement.
|
|
|
500
503
|
|
|
501
504
|
## Scale the evidence
|
|
502
505
|
|
|
503
|
-
- **A smoke test is not proof.** A single-digit
|
|
504
|
-
of rollouts only validates the wiring. Scale `
|
|
506
|
+
- **A smoke test is not proof.** A single-digit derived step count, a tiny dataset, or a handful
|
|
507
|
+
of rollouts only validates the wiring. Scale `epochs`, the dataset size,
|
|
505
508
|
and `group_size` to the model and the data you actually have before you trust a
|
|
506
509
|
result. Don't cite budget alone as the reason for an underpowered run.
|
|
507
510
|
- **Use the data you have.** Deliberately assign every usable row to training or to a
|
|
@@ -10,6 +10,7 @@ import math
|
|
|
10
10
|
from flash.catalog import samples_on_policy
|
|
11
11
|
from flash.cost.analytical import estimate_cost
|
|
12
12
|
from flash.cost.types import CostEstimate, RunConfig
|
|
13
|
+
from flash.engine.steps import on_policy_steps
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def _sft_epochs(spec) -> int:
|
|
@@ -19,6 +20,14 @@ def _sft_epochs(spec) -> int:
|
|
|
19
20
|
return int(t.epochs) if t.epochs is not None else RECIPE.sft.num_epochs
|
|
20
21
|
|
|
21
22
|
|
|
23
|
+
def _on_policy_epochs(spec) -> int:
|
|
24
|
+
from flash.engine.recipe import RECIPE
|
|
25
|
+
|
|
26
|
+
t = spec.train
|
|
27
|
+
default = RECIPE.rl.num_epochs if spec.algorithm == "grpo" else RECIPE.opd.num_epochs
|
|
28
|
+
return int(t.epochs) if t.epochs is not None else default
|
|
29
|
+
|
|
30
|
+
|
|
22
31
|
def _sft_seq_len(spec) -> int:
|
|
23
32
|
from flash.engine.recipe import RECIPE
|
|
24
33
|
|
|
@@ -41,6 +50,43 @@ def _sft_example_count(spec) -> int:
|
|
|
41
50
|
)
|
|
42
51
|
|
|
43
52
|
|
|
53
|
+
def _on_policy_example_count(spec) -> int:
|
|
54
|
+
t = spec.train
|
|
55
|
+
pinned_examples = int(t.max_examples) if t.max_examples else 0
|
|
56
|
+
if pinned_examples > 0:
|
|
57
|
+
return pinned_examples
|
|
58
|
+
env_examples = _env_max_examples(spec)
|
|
59
|
+
if env_examples > 0:
|
|
60
|
+
return env_examples
|
|
61
|
+
return _on_policy_requested_prompts_per_step(spec)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _env_max_examples(spec) -> int:
|
|
65
|
+
params = getattr(getattr(spec, "environment", None), "params", {}) or {}
|
|
66
|
+
if not isinstance(params, dict):
|
|
67
|
+
return 0
|
|
68
|
+
try:
|
|
69
|
+
value = int(params.get("max_examples") or 0)
|
|
70
|
+
except (TypeError, ValueError):
|
|
71
|
+
return 0
|
|
72
|
+
return max(0, value)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _on_policy_requested_prompts_per_step(spec) -> int:
|
|
76
|
+
from flash.engine.recipe import RECIPE
|
|
77
|
+
|
|
78
|
+
t = spec.train
|
|
79
|
+
default = (
|
|
80
|
+
RECIPE.rl.prompts_per_step if spec.algorithm == "grpo" else RECIPE.opd.prompts_per_step
|
|
81
|
+
)
|
|
82
|
+
return max(1, int(t.batch_size) if t.batch_size is not None else default)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _on_policy_prompts_per_step(spec, examples: int) -> int:
|
|
86
|
+
requested = _on_policy_requested_prompts_per_step(spec)
|
|
87
|
+
return max(1, min(requested, max(1, int(examples))))
|
|
88
|
+
|
|
89
|
+
|
|
44
90
|
def _sft_realized_batch(spec) -> int:
|
|
45
91
|
from flash.catalog import vocab_size_for
|
|
46
92
|
from flash.engine.recipe import RECIPE
|
|
@@ -68,22 +114,26 @@ def _sft_steps_from_examples(spec, examples: int, *, apply_cap: bool) -> int:
|
|
|
68
114
|
|
|
69
115
|
|
|
70
116
|
def spec_steps(spec) -> int:
|
|
71
|
-
"""Per-seed optimizer steps implied by a train spec (mirrors the worker).
|
|
72
|
-
(else recipe default). SFT: ``epochs x ceil(num_examples / realized_batch)`` capped by
|
|
73
|
-
``max_steps``, where ``num_examples`` must be pinned by ``max_examples``."""
|
|
74
|
-
from flash.engine.recipe import RECIPE
|
|
117
|
+
"""Per-seed optimizer steps implied by a train spec (mirrors the worker).
|
|
75
118
|
|
|
76
|
-
|
|
119
|
+
SFT: ``epochs x ceil(num_examples / realized_batch)`` capped by ``max_steps``. GRPO/OPD:
|
|
120
|
+
``epochs`` means passes over ``max_examples`` rows when pinned, otherwise the estimate uses one
|
|
121
|
+
prompt batch per epoch.
|
|
122
|
+
"""
|
|
77
123
|
if spec.algorithm == "grpo":
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
124
|
+
examples = _on_policy_example_count(spec)
|
|
125
|
+
return on_policy_steps(
|
|
126
|
+
epochs=_on_policy_epochs(spec),
|
|
127
|
+
prompt_count=examples,
|
|
128
|
+
prompts_per_step=_on_policy_prompts_per_step(spec, examples),
|
|
129
|
+
)
|
|
81
130
|
if spec.algorithm == "opd":
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
131
|
+
examples = _on_policy_example_count(spec)
|
|
132
|
+
return on_policy_steps(
|
|
133
|
+
epochs=_on_policy_epochs(spec),
|
|
134
|
+
prompt_count=examples,
|
|
135
|
+
prompts_per_step=_on_policy_prompts_per_step(spec, examples),
|
|
136
|
+
)
|
|
87
137
|
# max_examples is a CAP; 0 (like None) means "no cap" (worker trains the full dataset), so
|
|
88
138
|
# don't let max_examples=0 price a single step.
|
|
89
139
|
return _sft_steps_from_examples(spec, _sft_example_count(spec), apply_cap=True)
|
|
@@ -40,7 +40,7 @@ class RLConfig:
|
|
|
40
40
|
max_completion_len_thinking: int = 1536
|
|
41
41
|
prompts_per_step: int = 64
|
|
42
42
|
group_size: int = 8
|
|
43
|
-
|
|
43
|
+
num_epochs: int = 1
|
|
44
44
|
sampling_temperature: float = 1.0
|
|
45
45
|
sampling_top_p: float = 1.0
|
|
46
46
|
|
|
@@ -54,8 +54,6 @@ class OPDConfig:
|
|
|
54
54
|
# platform-managed and intentionally not configurable from [train].
|
|
55
55
|
teacher_model: str = "accounts/fireworks/models/glm-5p2"
|
|
56
56
|
teacher_base_url: str = "https://api.fireworks.ai/inference/v1"
|
|
57
|
-
# OPD is step-driven like GRPO (on-policy sampling), not epoch-driven like SFT.
|
|
58
|
-
num_steps: int = 100
|
|
59
57
|
learning_rate: float = 1e-5
|
|
60
58
|
max_prompt_len: int = 1024
|
|
61
59
|
max_completion_len: int = 512
|
|
@@ -65,6 +63,7 @@ class OPDConfig:
|
|
|
65
63
|
# Student samples per prompt. 1 is enough for a direct KD loss (no group-relative baseline
|
|
66
64
|
# as in GRPO); raise for more teacher-scored coverage per prompt at higher cost.
|
|
67
65
|
group_size: int = 1
|
|
66
|
+
num_epochs: int = 1
|
|
68
67
|
sampling_temperature: float = 1.0
|
|
69
68
|
sampling_top_p: float = 1.0
|
|
70
69
|
# Reverse-KL coefficient for the groupwise reverse-KL loss; scales the per-span
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Training-step derivation shared by worker and cost-estimate paths."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import math
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def on_policy_steps(
|
|
9
|
+
*,
|
|
10
|
+
epochs: int,
|
|
11
|
+
prompt_count: int,
|
|
12
|
+
prompts_per_step: int,
|
|
13
|
+
) -> int:
|
|
14
|
+
"""Resolve GRPO/OPD optimizer steps from full passes over the retained prompt pool."""
|
|
15
|
+
prompt_count = int(prompt_count)
|
|
16
|
+
if prompt_count <= 0:
|
|
17
|
+
raise ValueError("cannot derive epoch-based steps without at least one retained prompt")
|
|
18
|
+
return max(1, math.ceil(prompt_count * int(epochs) / max(1, int(prompts_per_step))))
|
|
@@ -29,6 +29,7 @@ from dataclasses import dataclass
|
|
|
29
29
|
|
|
30
30
|
from flash.engine.chalk_kernels import active_kernels, install_chalk_kernels
|
|
31
31
|
from flash.engine.recipe import RECIPE
|
|
32
|
+
from flash.engine.steps import on_policy_steps
|
|
32
33
|
from flash.engine.vram import opd_completion_len
|
|
33
34
|
from flash.engine.worker._pkg import W as _w
|
|
34
35
|
from flash.engine.worker.heartbeat import liveness_heartbeat
|
|
@@ -75,7 +76,7 @@ class OpdKnobs:
|
|
|
75
76
|
|
|
76
77
|
teacher_model: str = ""
|
|
77
78
|
teacher_base_url: str = ""
|
|
78
|
-
|
|
79
|
+
epochs: int = RECIPE.opd.num_epochs
|
|
79
80
|
learning_rate: float = 0.0
|
|
80
81
|
temperature: float = 0.0
|
|
81
82
|
top_p: float = 1.0
|
|
@@ -116,7 +117,7 @@ def _resolve_opd_knobs() -> OpdKnobs:
|
|
|
116
117
|
return OpdKnobs(
|
|
117
118
|
teacher_model=d.teacher_model,
|
|
118
119
|
teacher_base_url=d.teacher_base_url,
|
|
119
|
-
|
|
120
|
+
epochs=int(t.epochs) if t and t.epochs is not None else d.num_epochs,
|
|
120
121
|
learning_rate=float(opt("learning_rate", 0) or d.learning_rate),
|
|
121
122
|
temperature=float(
|
|
122
123
|
opt("temperature", None)
|
|
@@ -263,7 +264,7 @@ def run_opd():
|
|
|
263
264
|
warm_start = _w.JOB_SPEC.train.init_from_adapter if _w.JOB_SPEC else ""
|
|
264
265
|
print(
|
|
265
266
|
f"[opd] gkd (groupwise reverse-KL) teacher={knobs.teacher_model} "
|
|
266
|
-
f"
|
|
267
|
+
f"epochs={knobs.epochs} warm_start={warm_start or 'none'} "
|
|
267
268
|
f"mode={'multi-turn' if multi_turn else 'single-turn'}"
|
|
268
269
|
)
|
|
269
270
|
|
|
@@ -309,9 +310,12 @@ def run_opd():
|
|
|
309
310
|
"opd: the environment dataset is empty — no prompts to sample on-policy. Check the "
|
|
310
311
|
"environment's dataset()/train split before provisioning a GPU."
|
|
311
312
|
)
|
|
313
|
+
_max_examples = getattr(_w.JOB_SPEC.train, "max_examples", None) if _w.JOB_SPEC else None
|
|
314
|
+
max_examples = int(_max_examples or 0) if _max_examples is not None else 0
|
|
315
|
+
if max_examples > 0:
|
|
316
|
+
train = train[:max_examples]
|
|
312
317
|
rng = random.Random(_w.SEED)
|
|
313
318
|
rng.shuffle(train)
|
|
314
|
-
steps = knobs.steps
|
|
315
319
|
ppl_step = knobs.prompts_per_step
|
|
316
320
|
group = knobs.group_size
|
|
317
321
|
# Prompt budget mirrors GRPO: DROP (not truncate) prompts over the context budget, so the student
|
|
@@ -375,6 +379,21 @@ def run_opd():
|
|
|
375
379
|
f"[opd] filtered {n_over_budget}/{len(train)} prompts over the "
|
|
376
380
|
f"{prompt_budget}-token budget; pool = {len(examples)}"
|
|
377
381
|
)
|
|
382
|
+
if ppl_step > len(examples):
|
|
383
|
+
print(
|
|
384
|
+
f"[opd] lowering prompts_per_step from {ppl_step} to {len(examples)}: "
|
|
385
|
+
"only that many prompt(s) fit after filtering"
|
|
386
|
+
)
|
|
387
|
+
ppl_step = len(examples)
|
|
388
|
+
steps = on_policy_steps(
|
|
389
|
+
epochs=knobs.epochs,
|
|
390
|
+
prompt_count=len(examples),
|
|
391
|
+
prompts_per_step=ppl_step,
|
|
392
|
+
)
|
|
393
|
+
print(
|
|
394
|
+
f"[opd] epochs={knobs.epochs} over {len(examples)} retained prompt(s) at "
|
|
395
|
+
f"{ppl_step} prompts/step -> steps={steps}"
|
|
396
|
+
)
|
|
378
397
|
|
|
379
398
|
# Now that a non-empty on-policy pool is confirmed, prefetch the full base weights (deferred from
|
|
380
399
|
# setup so an all-over-budget dataset fails before this download). Still inside the setup phase
|
|
@@ -573,9 +592,12 @@ def run_opd():
|
|
|
573
592
|
# but opd only set attn_implementation at LOAD, so both the on-policy generate and the gkd loss
|
|
574
593
|
# forward ran under the default SDPA dispatch and silently lost the cuDNN kernel (codex[bot]).
|
|
575
594
|
# No-op (nullcontext) on non-Blackwell GPUs / when _attn isn't "sdpa".
|
|
595
|
+
def _samples_progress():
|
|
596
|
+
return samples_seen
|
|
597
|
+
|
|
576
598
|
with (
|
|
577
599
|
liveness_heartbeat(
|
|
578
|
-
"opd_step", progress=
|
|
600
|
+
"opd_step", progress=_samples_progress, fields=lambda: {"step": opt_steps}
|
|
579
601
|
),
|
|
580
602
|
_sdpa_cudnn_ctx(_attn),
|
|
581
603
|
):
|
|
@@ -633,6 +655,7 @@ def run_opd():
|
|
|
633
655
|
prompts.append(prompt_ids)
|
|
634
656
|
with liveness_heartbeat(
|
|
635
657
|
"opd_step",
|
|
658
|
+
progress=_samples_progress,
|
|
636
659
|
fields=lambda _step=opt_steps: {"step": _step},
|
|
637
660
|
keepalive=True,
|
|
638
661
|
):
|
|
@@ -804,6 +827,8 @@ def run_opd():
|
|
|
804
827
|
generated_tokens=generated_tokens,
|
|
805
828
|
notes={
|
|
806
829
|
"steps": steps,
|
|
830
|
+
"epochs": knobs.epochs,
|
|
831
|
+
"retained_prompts": len(examples),
|
|
807
832
|
# Optimizer steps actually applied; < steps if any iteration had no usable teacher
|
|
808
833
|
# signal (skipped). loss_curve length == opt_steps, so reporting stays honest.
|
|
809
834
|
"opt_steps": opt_steps,
|
|
@@ -976,8 +1001,11 @@ def _generate_one(*, model, tok, device, prompt_tensor, gen_cfg, knobs) -> _GenR
|
|
|
976
1001
|
def _gen_from_vllm_output(out: OpdVllmOutput, tok, knobs) -> _GenResult:
|
|
977
1002
|
"""Apply OPD's pre-scoring gates to one vLLM completion."""
|
|
978
1003
|
completion_ids = [int(t) for t in out.token_ids]
|
|
979
|
-
|
|
980
|
-
|
|
1004
|
+
decode = getattr(tok, "decode", None)
|
|
1005
|
+
completion_text = out.text or (
|
|
1006
|
+
decode(completion_ids, skip_special_tokens=True) if decode else ""
|
|
1007
|
+
)
|
|
1008
|
+
stop_text = decode(completion_ids, skip_special_tokens=False) if decode else completion_text
|
|
981
1009
|
if not (
|
|
982
1010
|
out.terminated
|
|
983
1011
|
or _rollout_terminated(
|
|
@@ -8,6 +8,7 @@ import time
|
|
|
8
8
|
|
|
9
9
|
from flash.engine.chalk_kernels import active_kernels, install_chalk_kernels
|
|
10
10
|
from flash.engine.recipe import RECIPE
|
|
11
|
+
from flash.engine.steps import on_policy_steps
|
|
11
12
|
from flash.engine.worker._pkg import W as _w
|
|
12
13
|
from flash.engine.worker.grpo import resolve_grpo_sleep_mode
|
|
13
14
|
from flash.engine.worker.heartbeat import liveness_heartbeat
|
|
@@ -54,11 +55,8 @@ def run_rl():
|
|
|
54
55
|
model_id = _w.JOB_SPEC.model if _w.JOB_SPEC else RECIPE.hf_model_id
|
|
55
56
|
download_seconds = _w.prefetch_model(model_id)
|
|
56
57
|
rl = RECIPE.rl
|
|
57
|
-
steps = int(
|
|
58
|
-
_w.JOB_SPEC.train.steps if _w.JOB_SPEC and _w.JOB_SPEC.train.steps is not None else rl.num_steps
|
|
59
|
-
)
|
|
60
|
-
gcfg = _w.grpo_overrides()
|
|
61
58
|
_t = _w.JOB_SPEC.train if _w.JOB_SPEC else None
|
|
59
|
+
gcfg = _w.grpo_overrides()
|
|
62
60
|
prompts_per_step = int(
|
|
63
61
|
_t.batch_size if _t and _t.batch_size is not None else rl.prompts_per_step
|
|
64
62
|
)
|
|
@@ -91,6 +89,10 @@ def run_rl():
|
|
|
91
89
|
tok.pad_token = tok.eos_token
|
|
92
90
|
|
|
93
91
|
train = env.dataset()
|
|
92
|
+
_max_examples = getattr(_t, "max_examples", None) if _t else None
|
|
93
|
+
max_examples = int(_max_examples or 0) if _max_examples is not None else 0
|
|
94
|
+
if max_examples > 0:
|
|
95
|
+
train = train[:max_examples]
|
|
94
96
|
rng = random.Random(_w.SEED)
|
|
95
97
|
rng.shuffle(train)
|
|
96
98
|
if conversational:
|
|
@@ -320,6 +322,16 @@ def run_rl():
|
|
|
320
322
|
print(
|
|
321
323
|
"WARN: generation batch not divisible by group size; check prompts_per_step/group_size"
|
|
322
324
|
)
|
|
325
|
+
epochs = int(_t.epochs) if _t and _t.epochs is not None else RECIPE.rl.num_epochs
|
|
326
|
+
steps = on_policy_steps(
|
|
327
|
+
epochs=epochs,
|
|
328
|
+
prompt_count=len(prompts),
|
|
329
|
+
prompts_per_step=batching["unique_prompts_per_step"],
|
|
330
|
+
)
|
|
331
|
+
print(
|
|
332
|
+
f"[rl] epochs={epochs} over {len(prompts)} retained prompt(s) at "
|
|
333
|
+
f"{batching['unique_prompts_per_step']} unique_prompts/step -> steps={steps}"
|
|
334
|
+
)
|
|
323
335
|
print(
|
|
324
336
|
f"[rl] GRPO batching: per_device={batching['per_device_train_batch_size']} "
|
|
325
337
|
f"grad_accum={batching['gradient_accumulation_steps']} "
|
|
@@ -692,6 +704,8 @@ def run_rl():
|
|
|
692
704
|
generated_tokens=gen_tokens,
|
|
693
705
|
notes={
|
|
694
706
|
"steps": steps,
|
|
707
|
+
"epochs": epochs,
|
|
708
|
+
"retained_prompts": len(prompts),
|
|
695
709
|
"resumed": bool(resume_ckpt),
|
|
696
710
|
"download_seconds": download_seconds,
|
|
697
711
|
"hf_transfer": os.environ.get("HF_HUB_ENABLE_HF_TRANSFER", ""),
|
|
@@ -235,10 +235,6 @@ def build_worker_env(
|
|
|
235
235
|
env["HF_REPO"] = spec.train.hf_repo
|
|
236
236
|
if getattr(spec.gpu, "network_volume", None):
|
|
237
237
|
env.update(weight_cache_env())
|
|
238
|
-
if spec.train.steps is not None:
|
|
239
|
-
env["RL_STEPS"] = str(spec.train.steps)
|
|
240
|
-
if spec.train.epochs is not None:
|
|
241
|
-
env["SFT_EPOCHS"] = str(spec.train.epochs)
|
|
242
238
|
for k in (
|
|
243
239
|
"SFT_PER_DEVICE_BS",
|
|
244
240
|
"VLLM_USE_V1",
|
|
@@ -190,7 +190,6 @@ _TOP_LEVEL_KEYS = frozenset(
|
|
|
190
190
|
)
|
|
191
191
|
_TRAIN_KEYS = frozenset(
|
|
192
192
|
{
|
|
193
|
-
"steps",
|
|
194
193
|
"epochs",
|
|
195
194
|
"lora_rank",
|
|
196
195
|
"lora_alpha",
|
|
@@ -333,7 +332,6 @@ def spec_from_dict(raw: dict[str, Any], run_id: str | None = None) -> JobSpec:
|
|
|
333
332
|
secrets=environment_secrets,
|
|
334
333
|
),
|
|
335
334
|
train=TrainSpec(
|
|
336
|
-
steps=_train_int(train_raw, "steps", minimum=1),
|
|
337
335
|
epochs=_train_int(train_raw, "epochs", minimum=1),
|
|
338
336
|
lora_rank=lora_rank,
|
|
339
337
|
lora_alpha=_train_int(train_raw, "lora_alpha", minimum=1) or 64,
|
|
@@ -379,9 +377,7 @@ def _validate_sft(spec: JobSpec) -> None:
|
|
|
379
377
|
|
|
380
378
|
|
|
381
379
|
def _validate_grpo(spec: JobSpec) -> None:
|
|
382
|
-
"""GRPO contract:
|
|
383
|
-
if spec.train.steps is not None and spec.train.steps <= 0:
|
|
384
|
-
raise ConfigError("train.steps must be positive for GRPO")
|
|
380
|
+
"""GRPO contract: epochs derive passes over retained prompts."""
|
|
385
381
|
if spec.train.group_size is not None and spec.train.group_size < 2:
|
|
386
382
|
raise ConfigError(
|
|
387
383
|
"train.group_size must be >= 2 for GRPO (TRL needs at least two generations "
|
|
@@ -390,13 +386,10 @@ def _validate_grpo(spec: JobSpec) -> None:
|
|
|
390
386
|
|
|
391
387
|
|
|
392
388
|
def _validate_opd(spec: JobSpec) -> None:
|
|
393
|
-
"""OPD contract:
|
|
389
|
+
"""OPD contract: epochs derive passes over retained prompts.
|
|
394
390
|
|
|
395
391
|
The teacher key (FIREWORKS_API_KEY) is a platform-owned credential the control plane injects into
|
|
396
392
|
the worker env (build_worker_env), like HF_TOKEN — never a user-declared secret."""
|
|
397
|
-
if spec.train.steps is not None and spec.train.steps <= 0:
|
|
398
|
-
# OPD is step-driven (on-policy sampling), like GRPO — not epoch-driven.
|
|
399
|
-
raise ConfigError("train.steps must be positive for opd")
|
|
400
393
|
if spec.train.max_context_tokens:
|
|
401
394
|
# Mirror run_opd's prompt-budget guard at PARSE time: a context budget that leaves no room
|
|
402
395
|
# for any prompt after the completion budget is rejected here, BEFORE a paid worker is
|
|
@@ -101,7 +101,6 @@ FIXED_SEED = 42
|
|
|
101
101
|
|
|
102
102
|
@dataclass(frozen=True)
|
|
103
103
|
class TrainSpec:
|
|
104
|
-
steps: int | None = None
|
|
105
104
|
epochs: int | None = None
|
|
106
105
|
lora_rank: int = 32
|
|
107
106
|
lora_alpha: int = 64
|
|
@@ -191,7 +190,6 @@ class JobSpec:
|
|
|
191
190
|
resolved_sha=str(env.get("resolved_sha") or ""),
|
|
192
191
|
),
|
|
193
192
|
train=TrainSpec(
|
|
194
|
-
steps=_opt_int(train.get("steps")),
|
|
195
193
|
epochs=_opt_int(train.get("epochs")),
|
|
196
194
|
lora_rank=int(train.get("lora_rank", 32)),
|
|
197
195
|
lora_alpha=int(train.get("lora_alpha", 64)),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "freesolo-flash-dev"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.49"
|
|
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).
|
|
@@ -106,7 +106,7 @@ artifacts = ["flash/py.typed"]
|
|
|
106
106
|
# auto-published by .github/workflows/publish-dev.yml: a push to `dev` whose version isn't yet on
|
|
107
107
|
# PyPI publishes it; pushes that don't change it find it already published and no-op. So cutting a
|
|
108
108
|
# dev-channel release = bump this (and [project].version) and merge to `dev`.
|
|
109
|
-
version = "0.2.
|
|
109
|
+
version = "0.2.49"
|
|
110
110
|
|
|
111
111
|
[tool.pytest.ini_options]
|
|
112
112
|
pythonpath = ["."]
|
{freesolo_flash_dev-0.2.48 → freesolo_flash_dev-0.2.49}/tests/test_agent_flash_cli_contract.py
RENAMED
|
@@ -99,7 +99,8 @@ def test_train_dry_run_emits_run_id_and_state(tmp_path: Path, capsys) -> None:
|
|
|
99
99
|
'id = "owner/env"\n'
|
|
100
100
|
"[train]\n"
|
|
101
101
|
'hf_repo = "owner/runs"\n'
|
|
102
|
-
"
|
|
102
|
+
"epochs = 1\n"
|
|
103
|
+
"max_examples = 10\n"
|
|
103
104
|
"[gpu]\n"
|
|
104
105
|
'type = "RTX 5090"\n'
|
|
105
106
|
)
|
|
@@ -23,7 +23,7 @@ def test_opd_algorithm_accepted():
|
|
|
23
23
|
"model": "Qwen/Qwen3.5-4B",
|
|
24
24
|
"algorithm": "opd",
|
|
25
25
|
"environment": {"id": "github:owner/repo@main:env/environment.py"},
|
|
26
|
-
"train": {"
|
|
26
|
+
"train": {"epochs": 1, "max_examples": 10, "hf_repo": "owner/runs"},
|
|
27
27
|
},
|
|
28
28
|
run_id="x",
|
|
29
29
|
)
|
|
@@ -55,7 +55,7 @@ def test_opd_capability_gated_per_model():
|
|
|
55
55
|
"model": "test/no-opd",
|
|
56
56
|
"algorithm": "opd",
|
|
57
57
|
"environment": {"id": "github:owner/repo@main:env/environment.py"},
|
|
58
|
-
"train": {"
|
|
58
|
+
"train": {"epochs": 1, "max_examples": 1, "hf_repo": "owner/runs"},
|
|
59
59
|
},
|
|
60
60
|
run_id="x",
|
|
61
61
|
)
|
|
@@ -84,7 +84,7 @@ def test_grpo_capability_still_enforced():
|
|
|
84
84
|
"model": "test/sft-only",
|
|
85
85
|
"algorithm": "grpo",
|
|
86
86
|
"environment": {"id": "github:owner/repo@main:env/environment.py"},
|
|
87
|
-
"train": {"
|
|
87
|
+
"train": {"epochs": 1, "max_examples": 1, "hf_repo": "owner/runs"},
|
|
88
88
|
},
|
|
89
89
|
run_id="x",
|
|
90
90
|
)
|
|
@@ -110,7 +110,7 @@ def test_qwen35_9b_now_supports_grpo():
|
|
|
110
110
|
"model": "Qwen/Qwen3.5-9B",
|
|
111
111
|
"algorithm": "grpo",
|
|
112
112
|
"environment": {"id": "github:owner/repo@main:env/environment.py"},
|
|
113
|
-
"train": {"
|
|
113
|
+
"train": {"epochs": 1, "max_examples": 1, "hf_repo": "owner/runs"},
|
|
114
114
|
"gpu": {"type": "A100 PCIe"},
|
|
115
115
|
},
|
|
116
116
|
run_id="x",
|
|
@@ -394,7 +394,7 @@ def test_observed_qwen2_opd_vllm_case_routes_off_32gb_cards(monkeypatch):
|
|
|
394
394
|
from flash.providers.base import get_gpu_info, provisional_gpu
|
|
395
395
|
|
|
396
396
|
monkeypatch.setattr(allocator, "available_providers", lambda: ("runpod",))
|
|
397
|
-
train = {"
|
|
397
|
+
train = {"epochs": 1, "max_completion_tokens": 128, "lora_rank": 32, "lora_alpha": 64}
|
|
398
398
|
|
|
399
399
|
need = required_vram_gb("Qwen/Qwen3.5-2B", "opd", train=train)
|
|
400
400
|
assert need > 40
|
|
@@ -433,7 +433,7 @@ def test_observed_qwen4b_opd_vllm_startup_case_routes_off_40gb_cards(monkeypatch
|
|
|
433
433
|
|
|
434
434
|
monkeypatch.setattr(allocator, "available_providers", lambda: ("runpod",))
|
|
435
435
|
train = {
|
|
436
|
-
"
|
|
436
|
+
"epochs": 1,
|
|
437
437
|
"max_context_tokens": 8192,
|
|
438
438
|
"max_completion_tokens": 128,
|
|
439
439
|
"lora_rank": 32,
|
|
@@ -488,14 +488,14 @@ def test_opd_catalog_model_config_gpu_matrix_routes_to_fitting_cards(monkeypatch
|
|
|
488
488
|
configs = {
|
|
489
489
|
# Matches the failed continuation shape from the OPD/vLLM RTX 5090 report.
|
|
490
490
|
"observed_2b_128tok_r32": {
|
|
491
|
-
"
|
|
491
|
+
"epochs": 1,
|
|
492
492
|
"max_completion_tokens": 128,
|
|
493
493
|
"lora_rank": 32,
|
|
494
494
|
"lora_alpha": 64,
|
|
495
495
|
},
|
|
496
|
-
"recipe_default": {"
|
|
496
|
+
"recipe_default": {"epochs": 1},
|
|
497
497
|
"opd_prompt_batch": {
|
|
498
|
-
"
|
|
498
|
+
"epochs": 1,
|
|
499
499
|
"batch_size": 8,
|
|
500
500
|
"group_size": 1,
|
|
501
501
|
"max_context_tokens": 1536,
|
|
@@ -503,7 +503,7 @@ def test_opd_catalog_model_config_gpu_matrix_routes_to_fitting_cards(monkeypatch
|
|
|
503
503
|
"lora_rank": 16,
|
|
504
504
|
},
|
|
505
505
|
"longer_context": {
|
|
506
|
-
"
|
|
506
|
+
"epochs": 1,
|
|
507
507
|
"batch_size": 8,
|
|
508
508
|
"group_size": 1,
|
|
509
509
|
"max_context_tokens": 4096,
|
|
@@ -511,7 +511,7 @@ def test_opd_catalog_model_config_gpu_matrix_routes_to_fitting_cards(monkeypatch
|
|
|
511
511
|
"lora_rank": 16,
|
|
512
512
|
},
|
|
513
513
|
"longer_completion": {
|
|
514
|
-
"
|
|
514
|
+
"epochs": 1,
|
|
515
515
|
"batch_size": 1,
|
|
516
516
|
"group_size": 1,
|
|
517
517
|
"max_context_tokens": 4096,
|
|
@@ -519,7 +519,7 @@ def test_opd_catalog_model_config_gpu_matrix_routes_to_fitting_cards(monkeypatch
|
|
|
519
519
|
"lora_rank": 16,
|
|
520
520
|
},
|
|
521
521
|
"wide_rollout_batch": {
|
|
522
|
-
"
|
|
522
|
+
"epochs": 1,
|
|
523
523
|
"batch_size": 8,
|
|
524
524
|
"group_size": 4,
|
|
525
525
|
"max_context_tokens": 4096,
|
|
@@ -538,7 +538,7 @@ def test_opd_catalog_model_config_gpu_matrix_routes_to_fitting_cards(monkeypatch
|
|
|
538
538
|
rc = RunConfig(
|
|
539
539
|
model_id,
|
|
540
540
|
"opd",
|
|
541
|
-
int(train.get("
|
|
541
|
+
int(train.get("epochs", 1)),
|
|
542
542
|
seq_len=train.get("max_context_tokens"),
|
|
543
543
|
completion_len=train.get("max_completion_tokens"),
|
|
544
544
|
batch_size=train.get("batch_size"),
|
|
@@ -675,7 +675,7 @@ def test_catalog_model_algorithm_config_gpu_matrix_resolves_to_fitting_cards(mon
|
|
|
675
675
|
for algo in ALGORITHMS:
|
|
676
676
|
if algo not in info.algos:
|
|
677
677
|
continue
|
|
678
|
-
train = {"epochs": 1, "max_examples": 8}
|
|
678
|
+
train = {"epochs": 1, "max_examples": 8}
|
|
679
679
|
need = allocator.required_vram_gb(model_id, algo, train=train, thinking=False)
|
|
680
680
|
expected_gpu = provisional_gpu(model_id, algo, train=train, thinking=False)
|
|
681
681
|
|