freesolo-flash-dev 1.0.1__tar.gz → 1.0.3__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.1 → freesolo_flash_dev-1.0.3}/.github/workflows/bake-kernel-cache.yml +5 -6
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/PKG-INFO +1 -1
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/catalog.py +54 -8
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/cli/commands.py +15 -6
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/cli/render.py +4 -1
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/cli/training_doc.py +48 -4
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/multiturn_rollout.py +130 -22
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/recipe.py +7 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/grpo.py +8 -2
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/opd.py +473 -75
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/opd_vllm.py +43 -7
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/packing.py +42 -11
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/perf/__init__.py +4 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/perf/memory.py +49 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/rl.py +96 -16
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/sft.py +82 -18
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/teacher.py +193 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/envs/adapter.py +3 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/envs/loader.py +1 -0
- freesolo_flash_dev-1.0.3/flash/multimodal.py +1004 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/_instance_poll.py +29 -17
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/_worker.py +1 -1
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/runner/__init__.py +157 -13
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/runner/deploy.py +418 -111
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/runner/lifecycle.py +193 -21
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/runner/verified_revisions.py +7 -1
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/serve/export.py +60 -17
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/serve/pricing.py +15 -6
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/_deps.py +35 -4
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/_runtime.py +191 -47
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/routes/runs.py +22 -6
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/pyproject.toml +2 -2
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_allocator.py +10 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_catalog_consistency.py +22 -2
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_cli_commands.py +1 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_cli_commands_coverage.py +3 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_cli_render_coverage.py +8 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_cost_models.py +2 -2
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_disk_gb.py +5 -9
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_env_push.py +19 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_export.py +78 -6
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_flash_mvp.py +5 -1
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_gpus.py +2 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_grpo_params.py +60 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_jobs.py +492 -44
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_kernel_fingerprint.py +12 -3
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_lora_rank_preflight.py +15 -5
- freesolo_flash_dev-1.0.3/tests/test_multimodal_input_grads.py +75 -0
- freesolo_flash_dev-1.0.3/tests/test_multimodal_training.py +969 -0
- freesolo_flash_dev-1.0.3/tests/test_multiturn_image_rollout.py +291 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_multiturn_rollout.py +1 -1
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_multiturn_rollout_coverage.py +1 -1
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_multiturn_rollout_request_policy.py +4 -4
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_opd.py +766 -15
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_opd_vllm.py +46 -1
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_packing.py +66 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_provider_routing.py +17 -2
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_resolve_params_b.py +1 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_resume_on_retry.py +63 -24
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_runmgmt.py +422 -24
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_serve.py +28 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_server_api.py +112 -4
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_serving_context_preflight.py +1 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_serving_contract.py +3 -2
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_sft_gc_off.py +7 -1
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_train_context_preflight.py +22 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_verified_revisions.py +29 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_verifiers.py +22 -2
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_weight_cache.py +14 -5
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_worker_image.py +17 -14
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/uv.lock +1 -1
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/.claude/skills/verify/SKILL.md +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/.dockerignore +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/.env.example +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/.github/workflows/auto-rebake.yml +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/.github/workflows/ci.yml +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/.github/workflows/main-source-guard.yml +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/.github/workflows/notify-tests-repo.yml +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/.github/workflows/publish-dev.yml +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/.github/workflows/publish-image.yml +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/.github/workflows/publish.yml +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/.github/workflows/version-parity.yml +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/.github/workflows/worker-image.yml +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/.gitignore +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/Dockerfile +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/Dockerfile.worker +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/LICENSE +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/README.md +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/build/kernel_cache/.gitignore +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/build/kernel_cache/.keep +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/docker/Dockerfile.kernelcache +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/docker/Dockerfile.kernelcache.relayer +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/docker/bake_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/docker/bake_pod_entry.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/docker/kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/docker/make_rp_handler.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/_channel.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/_fileio.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/_logging.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/_update_check.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/adapter_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/cli/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/cli/__main__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/cli/_tty.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/cli/env_setup.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/cli/envpush.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/client/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/client/config.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/client/http.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/client/runtime_secrets.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/client/specs.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/cost/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/cost/analytical.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/cost/facts.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/cost/spec.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/cost/types.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/accounting.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/chalk_kernels.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/steps.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/vram.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/__main__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/_pkg.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/adapter.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/decoding.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/finalize.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/gpu_setup.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/heartbeat.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/hf.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/kernel_warmup.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/lora.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/opd_gkd.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/perf/attn.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/perf/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/perf/lifecycle.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/perf/liger.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/perf/loraplus.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/rng.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/tokenizer_align.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker/wandb_log.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/engine/worker_entrypoint.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/envs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/envs/archive.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/envs/archive_policy.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/envs/base.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/envs/pull.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/envs/registry.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/lora_rank.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/opd_retry_contract.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/_auth.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/_deadline.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/_hf_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/_http.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/_instance.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/_instance_bootstrap.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/_instance_provider.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/_poll.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/allocator.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/base.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/lambdalabs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/lambdalabs/api.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/lambdalabs/auth.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/lambdalabs/gpus.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/lambdalabs/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/lambdalabs/preflight.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/lambdalabs/pricing.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/preflight.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/realized.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/runpod/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/runpod/api.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/runpod/auth.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/runpod/cost.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/runpod/gpus.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/runpod/jobs.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/runpod/keys.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/runpod/preflight.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/runpod/preload.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/runpod/pricing.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/runpod/slots.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/runpod/train/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/runpod/train/endpoints.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/vast/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/vast/api.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/vast/auth.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/vast/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/vast/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/vast/preflight.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/providers/vast/pricing.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/py.typed +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/runner/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/schema/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/schema/fields.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/serve/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/serve/deploy.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/serve/urls.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/__main__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/_locks.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/app.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/auth.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/billing.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/db.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/environment_registry.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/envs.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/reconcile.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/routes/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/routes/envs.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/routes/meta.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/routes/serving.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/server/run_registry.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/flash/spec.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/infisical-entrypoint.sh +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/scripts/build_dev_dist.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/_helpers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/_helpers/runner.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/_helpers/specs.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/_helpers/vast.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/conftest.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/fixtures/math_eval.jsonl +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/fixtures/math_train.jsonl +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/live/__init__.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/live/conftest.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/live/test_lambda_live.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/live/test_runpod_live.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/live/test_vast_live.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_accounting_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_agent_flash_cli_contract.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_algorithms.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_b200_rollout_opt.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_backend_jobspec_contract.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_base_model_provenance.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_cancel_remote.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_chalk_kernels.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_charge_pricing.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_cli_errors.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_cli_estimate.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_cli_help.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_cli_main_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_cli_managed.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_cli_render_theme.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_client.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_client_server_integration.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_config_overrides.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_controlled_experiment_repairs.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_cost_analytical.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_cost_equation.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_cost_estimate.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_cost_hardware.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_cost_rewards.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_dev_channel.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_endpoint_name.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_env_cache_evict.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_env_delete.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_env_download.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_env_download_api.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_env_no_install.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_env_publish.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_env_pull.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_env_pull_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_env_pull_loader_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_env_pull_managed_control_plane.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_env_rate_limit_resolve.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_flash_worker.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_flashinfer_cache_dirs.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_github_urlopen_retry.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_grpo_mask_aware.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_grpo_sleep_gate.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_health_liveness.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_idle_endpoint_reaper.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_instance_bootstrap_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_kv_util.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_lambda_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_lambda_runner.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_logging.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_login_perms.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_lora_rank_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_managed_hf_repo.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_metrics_schema_agent_contract.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_mig_guard.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_multiturn_rollout_records.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_oom_escalate_gpu.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_opd_full_state_resume.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_opd_resume_safety.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_open_model_policy.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_orchestrator_flash.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_packing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_poll_helpers.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_preflight.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_provider_preflight_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_provider_teardown_robustness.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_providers_symmetry.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_realized_cost.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_runpod_api_delete.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_runpod_key_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_runpod_key_waterfall.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_runpod_slots.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_server_app_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_server_billing.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_server_checkpoints_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_server_db.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_server_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_server_locks_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_serving_pricing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_serving_schema_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_sft_example_selection.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_sft_max_context.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_spec_and_validation.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_thinking_config.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_training_controls.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_update_check.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_vast_api.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_vast_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_vast_offers.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_vast_provider_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_vast_runner.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_version.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_vl_warmstart_adapter_keys.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_wandb_log_coverage.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_wandb_naming.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_warmstart_cross_repo.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_worker_dryrun.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_worker_hardexit.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_worker_init_heartbeat.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_worker_stack.py +0 -0
- {freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/tests/test_worker_thinking.py +0 -0
{freesolo_flash_dev-1.0.1 → freesolo_flash_dev-1.0.3}/.github/workflows/bake-kernel-cache.yml
RENAMED
|
@@ -22,7 +22,7 @@ on:
|
|
|
22
22
|
inputs:
|
|
23
23
|
sms:
|
|
24
24
|
description: "comma-separated sm list to bake; default is all validated arches"
|
|
25
|
-
default: "sm80,sm86,sm89,sm90,sm120"
|
|
25
|
+
default: "sm80,sm86,sm89,sm90,sm120,sm100"
|
|
26
26
|
required: false
|
|
27
27
|
# auto-rebake.yml calls this for the EXPENSIVE path (the cache is invalid and must be re-warmed on a
|
|
28
28
|
# real GPU). It passes the arch subset, the fingerprints to stamp, and a digest-pinned base so the
|
|
@@ -105,10 +105,9 @@ jobs:
|
|
|
105
105
|
gpu_type_id: "NVIDIA RTX PRO 6000 Blackwell Server Edition",
|
|
106
106
|
allowed_cuda: "13.0",
|
|
107
107
|
}
|
|
108
|
-
#
|
|
109
|
-
# good secure-cloud capacity
|
|
110
|
-
#
|
|
111
|
-
# passes, then add it to the default.
|
|
108
|
+
# datacenter blackwell (sm100, distinct from the sm120 rtx pro 6000). b200 has 180 gb,
|
|
109
|
+
# good secure-cloud capacity, and is validated. promote its image only after a successful
|
|
110
|
+
# real-b200 bake, then include it in default and automatic rebakes.
|
|
112
111
|
- {
|
|
113
112
|
sm: sm100,
|
|
114
113
|
arch: "10.0",
|
|
@@ -120,7 +119,7 @@ jobs:
|
|
|
120
119
|
- name: Should bake ${{ matrix.sm }}?
|
|
121
120
|
id: gate
|
|
122
121
|
run: |
|
|
123
|
-
req="${{ inputs.sms || 'sm80,sm86,sm89,sm90,sm120' }}"
|
|
122
|
+
req="${{ inputs.sms || 'sm80,sm86,sm89,sm90,sm120,sm100' }}"
|
|
124
123
|
req="${req// /}" # tolerate spaces, e.g. "sm80, sm86"
|
|
125
124
|
case ",$req," in
|
|
126
125
|
*,${{ matrix.sm }},*) echo "run=true" >> "$GITHUB_OUTPUT" ;;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: freesolo-flash-dev
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.3
|
|
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
|
|
@@ -13,6 +13,22 @@ ALGORITHMS = ("sft", "grpo", "opd")
|
|
|
13
13
|
# the cost model, VRAM sizing and worker allocator branch on — import ``samples_on_policy`` instead
|
|
14
14
|
# of hand-rolling the ("grpo", "opd") tuple at each site.
|
|
15
15
|
_ON_POLICY_ALGORITHMS = frozenset({"grpo", "opd"})
|
|
16
|
+
_IMAGE_TRAINING_MODELS = frozenset(
|
|
17
|
+
{
|
|
18
|
+
"Qwen/Qwen3.5-0.8B",
|
|
19
|
+
"Qwen/Qwen3.5-2B",
|
|
20
|
+
"Qwen/Qwen3.5-4B",
|
|
21
|
+
"Qwen/Qwen3.5-9B",
|
|
22
|
+
"Qwen/Qwen3.6-27B",
|
|
23
|
+
"Qwen/Qwen3.6-35B-A3B",
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def supports_image_training(model: str | ModelInfo | None) -> bool:
|
|
29
|
+
"""Return whether a curated model supports image-bearing SFT, GRPO, and OPD."""
|
|
30
|
+
model_id = model.id if isinstance(model, ModelInfo) else model
|
|
31
|
+
return bool(model_id and model_id in _IMAGE_TRAINING_MODELS)
|
|
16
32
|
|
|
17
33
|
|
|
18
34
|
def samples_on_policy(algorithm: str) -> bool:
|
|
@@ -146,10 +162,10 @@ class ModelInfo:
|
|
|
146
162
|
|
|
147
163
|
DEFAULT_MODEL = "Qwen/Qwen3.5-4B"
|
|
148
164
|
|
|
149
|
-
#
|
|
150
|
-
# ``src.prequant_config``).
|
|
151
|
-
#
|
|
152
|
-
#
|
|
165
|
+
# the pre-quantized fp8 checkpoint each base model's serving engine loads (mirrors serving's
|
|
166
|
+
# ``src.prequant_config``). every dense model serves a freesolo-owned fp8 checkpoint; the qwen3.6
|
|
167
|
+
# 35b-a3b moe serves the official qwen fp8 variant. informational for the catalog mirror; deploy
|
|
168
|
+
# gating reads only max_lora_rank.
|
|
153
169
|
SERVING_FP8_MODEL_REPOS: dict[str, str] = {
|
|
154
170
|
"openbmb/MiniCPM5-1B": "Freesolo-Co/MiniCPM5-1B-FP8",
|
|
155
171
|
"Qwen/Qwen3.5-0.8B": "Freesolo-Co/Qwen3.5-0.8B-FP8",
|
|
@@ -157,6 +173,7 @@ SERVING_FP8_MODEL_REPOS: dict[str, str] = {
|
|
|
157
173
|
"Qwen/Qwen3.5-4B": "Freesolo-Co/Qwen3.5-4B-FP8",
|
|
158
174
|
"Qwen/Qwen3.5-9B": "Freesolo-Co/Qwen3.5-9B-FP8",
|
|
159
175
|
"Qwen/Qwen3.6-35B-A3B": "Qwen/Qwen3.6-35B-A3B-FP8",
|
|
176
|
+
"Qwen/Qwen3.6-27B": "Freesolo-Co/Qwen3.6-27B-FP8",
|
|
160
177
|
}
|
|
161
178
|
|
|
162
179
|
MODELS: dict[str, ModelInfo] = {
|
|
@@ -182,7 +199,7 @@ MODELS: dict[str, ModelInfo] = {
|
|
|
182
199
|
"Qwen/Qwen3.5-0.8B": ModelInfo(
|
|
183
200
|
id="Qwen/Qwen3.5-0.8B",
|
|
184
201
|
display_name="Qwen3.5 0.8B",
|
|
185
|
-
params="0.9B
|
|
202
|
+
params="0.9B",
|
|
186
203
|
params_b=0.9,
|
|
187
204
|
vocab_size=248_320,
|
|
188
205
|
algos=ALGORITHMS,
|
|
@@ -201,7 +218,7 @@ MODELS: dict[str, ModelInfo] = {
|
|
|
201
218
|
"Qwen/Qwen3.5-2B": ModelInfo(
|
|
202
219
|
id="Qwen/Qwen3.5-2B",
|
|
203
220
|
display_name="Qwen3.5 2B",
|
|
204
|
-
params="2.3B
|
|
221
|
+
params="2.3B",
|
|
205
222
|
params_b=2.3,
|
|
206
223
|
vocab_size=248_320,
|
|
207
224
|
algos=ALGORITHMS,
|
|
@@ -219,7 +236,7 @@ MODELS: dict[str, ModelInfo] = {
|
|
|
219
236
|
"Qwen/Qwen3.5-4B": ModelInfo(
|
|
220
237
|
id="Qwen/Qwen3.5-4B",
|
|
221
238
|
display_name="Qwen3.5 4B",
|
|
222
|
-
params="4.7B
|
|
239
|
+
params="4.7B",
|
|
223
240
|
params_b=4.7,
|
|
224
241
|
vocab_size=248_320,
|
|
225
242
|
algos=ALGORITHMS,
|
|
@@ -241,7 +258,7 @@ MODELS: dict[str, ModelInfo] = {
|
|
|
241
258
|
"Qwen/Qwen3.5-9B": ModelInfo(
|
|
242
259
|
id="Qwen/Qwen3.5-9B",
|
|
243
260
|
display_name="Qwen3.5 9B",
|
|
244
|
-
params="9.7B
|
|
261
|
+
params="9.7B",
|
|
245
262
|
params_b=9.7,
|
|
246
263
|
vocab_size=248_320,
|
|
247
264
|
algos=ALGORITHMS,
|
|
@@ -264,6 +281,35 @@ MODELS: dict[str, ModelInfo] = {
|
|
|
264
281
|
"(two bf16 copies + KV + the 248k-vocab fp32 logits) needs an 80 GB-class card "
|
|
265
282
|
"(grpo_min_vram_gb floor).",
|
|
266
283
|
),
|
|
284
|
+
"Qwen/Qwen3.6-27B": ModelInfo(
|
|
285
|
+
id="Qwen/Qwen3.6-27B",
|
|
286
|
+
display_name="Qwen3.6 27B",
|
|
287
|
+
params="27B dense (multimodal VL, hybrid GDN)",
|
|
288
|
+
params_b=27.0,
|
|
289
|
+
num_layers=64,
|
|
290
|
+
hidden_size=5120,
|
|
291
|
+
vocab_size=248_320,
|
|
292
|
+
algos=("sft", "grpo", "opd"),
|
|
293
|
+
min_vram_gb=80,
|
|
294
|
+
sft_min_vram_gb=80,
|
|
295
|
+
quant="bf16",
|
|
296
|
+
recommended_gpu="A100 PCIe",
|
|
297
|
+
min_disk_gb=160,
|
|
298
|
+
serving=ServingCapacity(
|
|
299
|
+
gpu="H100",
|
|
300
|
+
serve_model_id=SERVING_FP8_MODEL_REPOS["Qwen/Qwen3.6-27B"],
|
|
301
|
+
max_loras=16,
|
|
302
|
+
max_lora_rank=64,
|
|
303
|
+
max_model_len=32768,
|
|
304
|
+
max_num_seqs=8,
|
|
305
|
+
gpu_memory_utilization=0.98,
|
|
306
|
+
),
|
|
307
|
+
thinking="hybrid",
|
|
308
|
+
notes="Dense 27B multimodal VL checkpoint with image-capable bf16 LoRA training. SFT fits "
|
|
309
|
+
"the 80GB A100 (~54GB weights); colocated GRPO needs the B200 (trainer + vLLM rollout = two "
|
|
310
|
+
"~54GB copies). Serves the owned VL-preserving FP8 on an H100 tier (dense, so no MoE expert "
|
|
311
|
+
"LoRA-buffer multiplier).",
|
|
312
|
+
),
|
|
267
313
|
"Qwen/Qwen3.6-35B-A3B": ModelInfo(
|
|
268
314
|
id="Qwen/Qwen3.6-35B-A3B",
|
|
269
315
|
display_name="Qwen3.6 35B-A3B (MoE)",
|
|
@@ -313,9 +313,8 @@ def cmd_train(args) -> int:
|
|
|
313
313
|
runtime_secrets_from_local_env(args.config, keys=spec.environment.secrets) or None
|
|
314
314
|
)
|
|
315
315
|
if args.dry_run:
|
|
316
|
-
# dry-run
|
|
317
|
-
#
|
|
318
|
-
# and charges nothing. a rejection surfaces as the server's error with exit status 1.
|
|
316
|
+
# dry-run runs submit-time server preflights without importing user code, allocating a gpu,
|
|
317
|
+
# or charging anything. a rejection surfaces as the server's error with exit status 1.
|
|
319
318
|
try:
|
|
320
319
|
status = client.create_run(
|
|
321
320
|
payload,
|
|
@@ -330,6 +329,14 @@ def cmd_train(args) -> int:
|
|
|
330
329
|
raise ApiError(exc.status, detail) from exc
|
|
331
330
|
compatibility = status.pop("train_schema_compatibility", None)
|
|
332
331
|
_print_train_schema_compatibility(compatibility)
|
|
332
|
+
print(
|
|
333
|
+
"dry-run validated: config/schema, model+algorithm compatibility, lora rank, "
|
|
334
|
+
"runtime-secret presence, warm-start source, serving context cap, and cost. it did NOT "
|
|
335
|
+
"import or run your environment.py; dataset loading, start_episode/episode shapes, "
|
|
336
|
+
"reward/scorer, worker imports, model load, and gpu/training are first exercised on the "
|
|
337
|
+
"worker after cold-start.",
|
|
338
|
+
file=sys.stderr,
|
|
339
|
+
)
|
|
333
340
|
if render.styled():
|
|
334
341
|
print(
|
|
335
342
|
render.object_panel(
|
|
@@ -700,7 +707,7 @@ def cmd_deployments(args) -> int:
|
|
|
700
707
|
return 0
|
|
701
708
|
print(
|
|
702
709
|
f"{'RUN ID':<30} {'STEP':<6} {'REVISION':<40} {'STATE':<14} "
|
|
703
|
-
f"{'VERIFIED AT':<
|
|
710
|
+
f"{'VERIFIED AT':<20} {'OPENAI MODEL':<30} DETAIL"
|
|
704
711
|
)
|
|
705
712
|
for row in rows:
|
|
706
713
|
deployment = row.get("deployment") or {}
|
|
@@ -708,14 +715,16 @@ def cmd_deployments(args) -> int:
|
|
|
708
715
|
step = deployment.get("checkpoint_step")
|
|
709
716
|
step_text = "final" if step is None else str(step)
|
|
710
717
|
verified_at = deployment.get("verified_at")
|
|
711
|
-
verified_text =
|
|
718
|
+
verified_text = (
|
|
719
|
+
"-" if verified_at is None else (render._humanize_ts(verified_at) or str(verified_at))
|
|
720
|
+
)
|
|
712
721
|
revision = str(deployment.get("adapter_revision") or "-")
|
|
713
722
|
state = str(deployment.get("state") or "-")
|
|
714
723
|
openai_model = str(deployment.get("openai_model") or run_id)
|
|
715
724
|
detail = str(deployment.get("error") or deployment.get("detail") or "")[:160]
|
|
716
725
|
print(
|
|
717
726
|
f"{run_id:<30} {step_text:<6} {revision:<40} {state:<14} "
|
|
718
|
-
f"{verified_text:<
|
|
727
|
+
f"{verified_text:<20} {openai_model:<30} {detail}"
|
|
719
728
|
)
|
|
720
729
|
return 0
|
|
721
730
|
|
|
@@ -488,7 +488,10 @@ def deployments_table(rows: list[dict]) -> str:
|
|
|
488
488
|
("final" if step is None else str(step), _TEAL),
|
|
489
489
|
(str(deployment.get("adapter_revision") or "-"), _ACCENT2),
|
|
490
490
|
(state, color),
|
|
491
|
-
(
|
|
491
|
+
(
|
|
492
|
+
"-" if verified_at is None else (_humanize_ts(verified_at) or str(verified_at)),
|
|
493
|
+
_GRAY,
|
|
494
|
+
),
|
|
492
495
|
(str(deployment.get("openai_model") or run_id), _GREEN),
|
|
493
496
|
(detail, _GRAY),
|
|
494
497
|
]
|
|
@@ -154,6 +154,47 @@ flash undeploy <run-id> # tear the endpoint down
|
|
|
154
154
|
flash export --adapter-id <run-id> --repository <you>/<repo> # copy adapter weights to your HF repo
|
|
155
155
|
```
|
|
156
156
|
|
|
157
|
+
### Loading an exported adapter locally (transformers + peft)
|
|
158
|
+
|
|
159
|
+
Flash trains the Qwen3.5/3.6 family against the full multimodal module tree, so adapter
|
|
160
|
+
weights saved by the worker carry a `language_model.` infix in their tensor keys
|
|
161
|
+
(`base_model.model.model.language_model.layers.*`). During `flash export`, adapters that
|
|
162
|
+
can be represented in the text-only namespace are normalized to
|
|
163
|
+
`base_model.model.model.layers.*`. When an export retains non-LM tensors, it keeps the
|
|
164
|
+
multimodal namespace instead. Do not infer the namespace solely from whether `flash export`
|
|
165
|
+
was used: inspect the keys. For normalized keys, load with vanilla peft and
|
|
166
|
+
`AutoModelForCausalLM`:
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from peft import PeftModel
|
|
170
|
+
from transformers import AutoModelForCausalLM
|
|
171
|
+
|
|
172
|
+
base = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3.5-0.8B") # qwen3.5 causal-lm class
|
|
173
|
+
model = PeftModel.from_pretrained(base, "<you>/<repo>")
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Before you trust any local eval, check the key namespace matches the model class you
|
|
177
|
+
loaded.** peft does NOT error on mismatched keys — it emits a `UserWarning` about missing
|
|
178
|
+
adapter keys and silently applies *nothing*, so you would benchmark the bare base model
|
|
179
|
+
believing it is your adapter. If the inspected keys carry the `language_model.` infix,
|
|
180
|
+
load the base with the multimodal class whose parameters live under that namespace instead:
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
# infixed keys (base_model.model.model.language_model.layers.*) need the multimodal class:
|
|
184
|
+
from transformers import AutoModelForImageTextToText
|
|
185
|
+
|
|
186
|
+
base = AutoModelForImageTextToText.from_pretrained("Qwen/Qwen3.5-0.8B")
|
|
187
|
+
# qwen3.5/3.6 dense models resolve to Qwen3_5ForConditionalGeneration
|
|
188
|
+
# qwen3.6-35b-a3b (moe) resolves to Qwen3_5MoeForConditionalGeneration
|
|
189
|
+
model = PeftModel.from_pretrained(base, "<you>/<repo>")
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
To check which form you have, read the safetensors key names (stdlib, no torch needed):
|
|
193
|
+
the JSON header of `adapter_model.safetensors` lists every tensor key. Rule of thumb:
|
|
194
|
+
`model.layers.*` keys pair with `AutoModelForCausalLM`; `model.language_model.layers.*`
|
|
195
|
+
keys pair with the `*ForConditionalGeneration` class. After loading, confirm the adapter
|
|
196
|
+
actually applied: outputs (or a probe metric) must differ from the bare base model.
|
|
197
|
+
|
|
157
198
|
The rest of this file is about doing the above *well* — designing a reward that teaches,
|
|
158
199
|
and deciding honestly whether a run improved.
|
|
159
200
|
|
|
@@ -167,9 +208,11 @@ Work in tight, attributable iterations. Each one is a hypothesis:
|
|
|
167
208
|
1. Reconstruct state — what's the best run so far, and what have you already tried?
|
|
168
209
|
2. Form a hypothesis — pick ONE lever and say WHY it will move the metric.
|
|
169
210
|
3. Change that ONE lever.
|
|
170
|
-
4. Validate
|
|
171
|
-
|
|
172
|
-
|
|
211
|
+
4. Validate with `flash train configs/sft.toml --dry-run`. This server-side preview checks
|
|
212
|
+
config/schema, model+algorithm compatibility, LoRA rank, runtime-secret presence, warm-start
|
|
213
|
+
source, serving context cap, and cost for free, with no GPU or charge. It does not import or run
|
|
214
|
+
`environment.py`; dataset loading, episode shapes, reward/scorer, worker imports, model load, and
|
|
215
|
+
GPU/training are first exercised on the worker after cold-start.
|
|
173
216
|
5. Submit — `flash train configs/sft.toml`.
|
|
174
217
|
6. Judge — read the metric trend AND a sample of real rollouts (see below).
|
|
175
218
|
7. Keep the best run; revert the change if it didn't beat the noise band. Repeat.
|
|
@@ -206,7 +249,7 @@ spending another GPU run:
|
|
|
206
249
|
|
|
207
250
|
| Issue | Symptom | Mitigation |
|
|
208
251
|
| --- | --- | --- |
|
|
209
|
-
| Environment id is blank or stale | `flash train --dry-run`
|
|
252
|
+
| Environment id is blank or stale | A blank id fails config validation. A stale published id can pass `flash train --dry-run` because dry-run does not import or run `environment.py`; the worker then 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. |
|
|
210
253
|
| 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. |
|
|
211
254
|
| 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]`. |
|
|
212
255
|
| 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. |
|
|
@@ -221,6 +264,7 @@ spending another GPU run:
|
|
|
221
264
|
| Run looks stuck after disconnecting | Terminal stopped streaming but the job may still be alive | Ctrl-C detaches. Use `flash log <run-id> --follow` to reattach, `flash log <run-id>` for the console/error output, or `flash cancel <run-id>` if you intentionally want to stop it. |
|
|
222
265
|
| 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. |
|
|
223
266
|
| 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`. |
|
|
267
|
+
| Exported adapter is a silent no-op locally | peft warns about missing adapter keys and local eval matches the bare base model | The adapter's key namespace does not match the loaded model class. `model.layers.*` keys pair with `AutoModelForCausalLM`; `model.language_model.layers.*` keys pair with `Qwen3_5ForConditionalGeneration` / `Qwen3_5MoeForConditionalGeneration` (via `AutoModelForImageTextToText`). See "Loading an exported adapter locally". |
|
|
224
268
|
| 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. |
|
|
225
269
|
| 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. |
|
|
226
270
|
|
|
@@ -326,6 +326,7 @@ class _RolloutState:
|
|
|
326
326
|
"done",
|
|
327
327
|
"env_mask",
|
|
328
328
|
"example",
|
|
329
|
+
"images",
|
|
329
330
|
"logprobs",
|
|
330
331
|
"messages",
|
|
331
332
|
"prompt_ids",
|
|
@@ -333,8 +334,9 @@ class _RolloutState:
|
|
|
333
334
|
"turns",
|
|
334
335
|
)
|
|
335
336
|
|
|
336
|
-
def __init__(self, example, messages, prompt_ids, state, budget):
|
|
337
|
+
def __init__(self, example, messages, prompt_ids, state, budget, images=None):
|
|
337
338
|
self.example = example
|
|
339
|
+
self.images = images
|
|
338
340
|
self.messages = messages
|
|
339
341
|
self.prompt_ids = prompt_ids
|
|
340
342
|
self.cur_ids = list(prompt_ids) # invariant: cur_ids == prompt_ids + completion_ids so far
|
|
@@ -398,23 +400,56 @@ def _advance_after_turn(
|
|
|
398
400
|
r.cur_ids.extend(glue)
|
|
399
401
|
|
|
400
402
|
|
|
403
|
+
def _extract_prompt_images(messages: list[dict]) -> list[object]:
|
|
404
|
+
"""Extract decoded PIL images from TRL-injected prompt content blocks."""
|
|
405
|
+
from PIL import Image
|
|
406
|
+
|
|
407
|
+
images = []
|
|
408
|
+
for message in messages:
|
|
409
|
+
content = message.get("content")
|
|
410
|
+
if not isinstance(content, list):
|
|
411
|
+
continue
|
|
412
|
+
for part in content:
|
|
413
|
+
if not isinstance(part, dict) or part.get("type") not in {
|
|
414
|
+
"image",
|
|
415
|
+
"image_url",
|
|
416
|
+
"input_image",
|
|
417
|
+
}:
|
|
418
|
+
continue
|
|
419
|
+
image = part.get("image")
|
|
420
|
+
if not isinstance(image, Image.Image):
|
|
421
|
+
raise ValueError(
|
|
422
|
+
"multimodal rollout image blocks must carry a PIL image at part['image']"
|
|
423
|
+
)
|
|
424
|
+
images.append(image)
|
|
425
|
+
return images
|
|
426
|
+
|
|
427
|
+
|
|
401
428
|
def _build_rollout_states(
|
|
402
429
|
examples: list[dict],
|
|
403
430
|
active_env,
|
|
404
431
|
render: Callable[[list, bool], list[int]],
|
|
405
432
|
engine_max_len: int | None,
|
|
433
|
+
*,
|
|
434
|
+
extract_images: bool = False,
|
|
435
|
+
rollout_prompts: list[list[dict]] | None = None,
|
|
406
436
|
) -> list[_RolloutState]:
|
|
407
437
|
"""Initialise one :class:`_RolloutState` per example for :func:`rollout_async`."""
|
|
438
|
+
if rollout_prompts is not None and len(rollout_prompts) != len(examples):
|
|
439
|
+
raise ValueError("rollout prompts must align one-to-one with examples")
|
|
408
440
|
rollouts: list[_RolloutState] = []
|
|
409
|
-
for example in examples:
|
|
441
|
+
for index, example in enumerate(examples):
|
|
410
442
|
state = active_env.new_rollout_state(example)
|
|
411
443
|
initial_messages = state.get("prompt") or state.get("messages")
|
|
444
|
+
if rollout_prompts is not None:
|
|
445
|
+
initial_messages = rollout_prompts[index]
|
|
412
446
|
if not isinstance(initial_messages, list):
|
|
413
447
|
raise KeyError("multi-turn rollout state must include prompt or messages")
|
|
414
448
|
messages = [dict(m) for m in initial_messages]
|
|
449
|
+
images = _extract_prompt_images(messages) if extract_images else None
|
|
415
450
|
prompt_ids = render(messages, True)
|
|
416
451
|
budget = (engine_max_len - len(prompt_ids) - 8) if engine_max_len else None
|
|
417
|
-
rollouts.append(_RolloutState(example, messages, prompt_ids, state, budget))
|
|
452
|
+
rollouts.append(_RolloutState(example, messages, prompt_ids, state, budget, images))
|
|
418
453
|
return rollouts
|
|
419
454
|
|
|
420
455
|
|
|
@@ -481,6 +516,7 @@ class _LogicalRequest:
|
|
|
481
516
|
prefix_ids: tuple[int, ...]
|
|
482
517
|
max_tokens: int
|
|
483
518
|
initial: bool
|
|
519
|
+
images: list[object] | None
|
|
484
520
|
attempts: int = 0
|
|
485
521
|
started_at: float = 0.0
|
|
486
522
|
|
|
@@ -490,7 +526,7 @@ def rollout_async(
|
|
|
490
526
|
examples: list[dict],
|
|
491
527
|
active_env,
|
|
492
528
|
render: Callable[[list, bool], list[int]],
|
|
493
|
-
submit: Callable[[str, list[int], int, bool], None],
|
|
529
|
+
submit: Callable[[str, list[int], int, bool, list[object] | None], None],
|
|
494
530
|
poll: Callable[[], list[RolloutCompletion]],
|
|
495
531
|
busy: Callable[[], bool],
|
|
496
532
|
abort: Callable[[list[str]], None],
|
|
@@ -498,6 +534,8 @@ def rollout_async(
|
|
|
498
534
|
max_turns: int,
|
|
499
535
|
per_turn_max_tokens: int,
|
|
500
536
|
engine_max_len: int | None = None,
|
|
537
|
+
extract_images: bool = False,
|
|
538
|
+
rollout_prompts: list[list[dict]] | None = None,
|
|
501
539
|
request_timeout_seconds: float | None = None,
|
|
502
540
|
request_max_attempts: int = 2,
|
|
503
541
|
monotonic: Callable[[], float] = time.monotonic,
|
|
@@ -516,7 +554,14 @@ def rollout_async(
|
|
|
516
554
|
if request_timeout_seconds is not None and request_timeout_seconds <= 0:
|
|
517
555
|
raise ValueError("request_timeout_seconds must be positive when set")
|
|
518
556
|
|
|
519
|
-
rollouts = _build_rollout_states(
|
|
557
|
+
rollouts = _build_rollout_states(
|
|
558
|
+
examples,
|
|
559
|
+
active_env,
|
|
560
|
+
render,
|
|
561
|
+
engine_max_len,
|
|
562
|
+
extract_images=extract_images,
|
|
563
|
+
rollout_prompts=rollout_prompts,
|
|
564
|
+
)
|
|
520
565
|
by_id: dict[str, _LogicalRequest] = {}
|
|
521
566
|
to_env: queue.Queue = queue.Queue()
|
|
522
567
|
to_submit: queue.Queue = queue.Queue()
|
|
@@ -527,7 +572,13 @@ def rollout_async(
|
|
|
527
572
|
logical.started_at = monotonic()
|
|
528
573
|
by_id[req_id] = logical
|
|
529
574
|
try:
|
|
530
|
-
submit(
|
|
575
|
+
submit(
|
|
576
|
+
req_id,
|
|
577
|
+
list(logical.prefix_ids),
|
|
578
|
+
logical.max_tokens,
|
|
579
|
+
logical.initial,
|
|
580
|
+
logical.images,
|
|
581
|
+
)
|
|
531
582
|
except Exception:
|
|
532
583
|
by_id.pop(req_id, None)
|
|
533
584
|
with contextlib.suppress(Exception):
|
|
@@ -541,6 +592,7 @@ def rollout_async(
|
|
|
541
592
|
prefix_ids=tuple(prefix),
|
|
542
593
|
max_tokens=int(max_new),
|
|
543
594
|
initial=bool(initial),
|
|
595
|
+
images=r.images,
|
|
544
596
|
)
|
|
545
597
|
)
|
|
546
598
|
|
|
@@ -718,6 +770,8 @@ def build_rollout_func(
|
|
|
718
770
|
active_env,
|
|
719
771
|
tok,
|
|
720
772
|
examples_by_key: dict,
|
|
773
|
+
processor=None,
|
|
774
|
+
multimodal: bool = False,
|
|
721
775
|
max_completion: int,
|
|
722
776
|
max_turns: int,
|
|
723
777
|
temperature: float,
|
|
@@ -751,18 +805,47 @@ def build_rollout_func(
|
|
|
751
805
|
|
|
752
806
|
_SOParams = StructuredOutputsParams
|
|
753
807
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
808
|
+
image_pad_id = None
|
|
809
|
+
if multimodal:
|
|
810
|
+
if processor is None:
|
|
811
|
+
raise ValueError("multimodal rollout requires a processor")
|
|
812
|
+
from flash.multimodal import (
|
|
813
|
+
collapse_image_pad_runs,
|
|
814
|
+
multimodal_prompt_key,
|
|
815
|
+
resolve_image_pad_token_id,
|
|
759
816
|
)
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
817
|
+
|
|
818
|
+
image_pad_id = resolve_image_pad_token_id(processor, tok)
|
|
819
|
+
|
|
820
|
+
def render(messages: list, add_generation_prompt: bool) -> list[int]:
|
|
821
|
+
rendered = processor.apply_chat_template(
|
|
822
|
+
messages,
|
|
823
|
+
add_generation_prompt=add_generation_prompt,
|
|
824
|
+
tokenize=True,
|
|
825
|
+
return_dict=True,
|
|
826
|
+
enable_thinking=thinking,
|
|
827
|
+
)
|
|
828
|
+
input_ids = rendered["input_ids"]
|
|
829
|
+
if hasattr(input_ids, "tolist"):
|
|
830
|
+
input_ids = input_ids.tolist()
|
|
831
|
+
ids = list(input_ids)
|
|
832
|
+
if ids and isinstance(ids[0], (list, tuple)):
|
|
833
|
+
ids = list(ids[0])
|
|
834
|
+
return [int(token_id) for token_id in ids]
|
|
835
|
+
|
|
836
|
+
else:
|
|
837
|
+
_render_cache = _LRUCache(8192)
|
|
838
|
+
|
|
839
|
+
def render(messages: list, add_generation_prompt: bool) -> list[int]:
|
|
840
|
+
cache_key = (
|
|
841
|
+
f"{add_generation_prompt}\x00{json.dumps(messages, sort_keys=True, default=str)}"
|
|
842
|
+
)
|
|
843
|
+
cached = _render_cache.get(cache_key)
|
|
844
|
+
if cached is not None:
|
|
845
|
+
return cached
|
|
846
|
+
ids = render_message_ids(tok, messages, add_generation_prompt, thinking=thinking)
|
|
847
|
+
_render_cache.put(cache_key, ids)
|
|
848
|
+
return ids
|
|
766
849
|
|
|
767
850
|
env_glue = make_env_glue(tok, thinking=thinking)
|
|
768
851
|
|
|
@@ -774,7 +857,13 @@ def build_rollout_func(
|
|
|
774
857
|
vocab_size = _engine_vocab_size(engine)
|
|
775
858
|
active_ids: set[str] = set()
|
|
776
859
|
|
|
777
|
-
def submit(
|
|
860
|
+
def submit(
|
|
861
|
+
req_id: str,
|
|
862
|
+
prefix_ids: list[int],
|
|
863
|
+
max_tokens: int,
|
|
864
|
+
initial: bool,
|
|
865
|
+
images: list[object] | None,
|
|
866
|
+
) -> None:
|
|
778
867
|
"""Enqueue one assistant-turn request."""
|
|
779
868
|
if not prefix_ids:
|
|
780
869
|
raise ValueError(
|
|
@@ -801,9 +890,21 @@ def build_rollout_func(
|
|
|
801
890
|
# env contract has no per-turn schema channel; unconstrained env/tool turns are
|
|
802
891
|
# not generated here.
|
|
803
892
|
sp_kwargs["structured_outputs"] = _SOParams(**copy.deepcopy(structured_outputs))
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
893
|
+
if images:
|
|
894
|
+
if image_pad_id is None:
|
|
895
|
+
raise ValueError("multimodal rollout is missing the image-pad token id")
|
|
896
|
+
collapsed = collapse_image_pad_runs(list(prefix_ids), image_pad_id, len(images))
|
|
897
|
+
prompt = {
|
|
898
|
+
"prompt_token_ids": collapsed,
|
|
899
|
+
"multi_modal_data": {"image": images if len(images) > 1 else images[0]},
|
|
900
|
+
}
|
|
901
|
+
# suppress the image-pad token during generation: a stray pad emitted into the
|
|
902
|
+
# assistant turn would corrupt the next turn's collapse_image_pad_runs count and
|
|
903
|
+
# misalign vision data, aborting the rollout. mirrors the single-turn opd guard.
|
|
904
|
+
sp_kwargs["logit_bias"] = {int(image_pad_id): -100.0}
|
|
905
|
+
else:
|
|
906
|
+
prompt = {"prompt_token_ids": list(prefix_ids)}
|
|
907
|
+
llm_engine.add_request(req_id, prompt, SamplingParams(**sp_kwargs))
|
|
807
908
|
active_ids.add(req_id)
|
|
808
909
|
|
|
809
910
|
def poll() -> list[RolloutCompletion]:
|
|
@@ -838,7 +939,12 @@ def build_rollout_func(
|
|
|
838
939
|
if sleep_mode:
|
|
839
940
|
engine.wake_up(tags=["kv_cache"])
|
|
840
941
|
woke = True
|
|
841
|
-
|
|
942
|
+
if multimodal:
|
|
943
|
+
examples = [
|
|
944
|
+
examples_by_key.get(multimodal_prompt_key(p), {"prompt": p}) for p in prompts
|
|
945
|
+
]
|
|
946
|
+
else:
|
|
947
|
+
examples = [examples_by_key.get(_prompt_key(p), {"prompt": p}) for p in prompts]
|
|
842
948
|
rollouts = rollout_async(
|
|
843
949
|
examples=examples,
|
|
844
950
|
active_env=active_env,
|
|
@@ -850,6 +956,8 @@ def build_rollout_func(
|
|
|
850
956
|
max_turns=max_turns,
|
|
851
957
|
per_turn_max_tokens=max_completion,
|
|
852
958
|
engine_max_len=engine_max_len,
|
|
959
|
+
extract_images=multimodal,
|
|
960
|
+
rollout_prompts=prompts if multimodal else None,
|
|
853
961
|
abort=abort,
|
|
854
962
|
request_timeout_seconds=request_timeout_seconds,
|
|
855
963
|
request_max_attempts=request_max_attempts,
|
|
@@ -35,6 +35,7 @@ class TeacherModel:
|
|
|
35
35
|
# serverless models — confirm against the live fireworks.ai pricing before relying on the
|
|
36
36
|
# `flash train` cost quote.
|
|
37
37
|
usd_per_1m: tuple[float, float]
|
|
38
|
+
supports_images: bool = False
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
# The alias used when [train] omits teacher_model — the historical fixed teacher, unchanged.
|
|
@@ -64,6 +65,7 @@ TEACHER_MODELS: dict[str, TeacherModel] = {
|
|
|
64
65
|
model_id="accounts/fireworks/models/kimi-k2p6",
|
|
65
66
|
display_name="Kimi K2.6",
|
|
66
67
|
usd_per_1m=(0.95, 4.00),
|
|
68
|
+
supports_images=True,
|
|
67
69
|
),
|
|
68
70
|
}
|
|
69
71
|
|
|
@@ -75,6 +77,11 @@ def normalize_teacher_alias(value: str) -> str:
|
|
|
75
77
|
return "-".join(str(value).strip().lower().replace("_", " ").replace("-", " ").split())
|
|
76
78
|
|
|
77
79
|
|
|
80
|
+
def teacher_supports_images(value: str) -> bool:
|
|
81
|
+
"""Return whether the resolved managed teacher accepts image-conditioned echo scoring."""
|
|
82
|
+
return resolve_teacher(value).supports_images
|
|
83
|
+
|
|
84
|
+
|
|
78
85
|
def resolve_teacher(value: str) -> TeacherModel:
|
|
79
86
|
"""Resolve a ``[train] teacher_model`` value to its catalog entry.
|
|
80
87
|
|
|
@@ -45,10 +45,16 @@ def build_grpo_prompt_dataset(prompts: list[dict]) -> tuple[list[dict], list]:
|
|
|
45
45
|
|
|
46
46
|
Dataset.from_list infers one type per field across ALL rows; mixed-type metadata
|
|
47
47
|
(e.g. int vs str in the same field) causes ArrowInvalid at RL startup. Fix: keep only
|
|
48
|
-
trivially
|
|
48
|
+
trivially typed columns (prompt, integer example_idx, and optional image descriptor strings);
|
|
49
|
+
reward_fn maps the index back.
|
|
49
50
|
"""
|
|
50
51
|
examples = [p["example"] for p in prompts]
|
|
51
|
-
rows = [
|
|
52
|
+
rows = []
|
|
53
|
+
for i, prompt in enumerate(prompts):
|
|
54
|
+
row = {"prompt": prompt["prompt"], "example_idx": i}
|
|
55
|
+
if "images" in prompt:
|
|
56
|
+
row["images"] = list(prompt["images"])
|
|
57
|
+
rows.append(row)
|
|
52
58
|
return rows, examples
|
|
53
59
|
|
|
54
60
|
|