freesolo-flash-dev 1.0.2__tar.gz → 1.0.4__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.2 → freesolo_flash_dev-1.0.4}/.github/workflows/bake-kernel-cache.yml +5 -6
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/Dockerfile.worker +1 -1
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/PKG-INFO +4 -4
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/catalog.py +54 -8
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/cli/__init__.py +5 -1
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/cli/commands.py +31 -20
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/cli/envpush.py +192 -54
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/cli/render.py +80 -14
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/cli/training_doc.py +6 -4
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/client/http.py +24 -10
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/multiturn_rollout.py +130 -22
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/recipe.py +7 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/__init__.py +6 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/grpo.py +8 -2
- freesolo_flash_dev-1.0.4/flash/engine/worker/grpo_multimodal.py +274 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/hf.py +342 -119
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/opd.py +510 -81
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/opd_vllm.py +43 -7
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/packing.py +42 -11
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/perf/__init__.py +4 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/perf/memory.py +49 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/rl.py +114 -17
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/sft.py +82 -18
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/teacher.py +304 -1
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/envs/adapter.py +3 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/envs/loader.py +1 -0
- freesolo_flash_dev-1.0.4/flash/multimodal.py +1004 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/_instance_poll.py +29 -17
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/_worker.py +6 -2
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/runner/__init__.py +157 -13
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/runner/deploy.py +418 -111
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/runner/lifecycle.py +193 -21
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/schema/__init__.py +35 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/serve/pricing.py +15 -6
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/_runtime.py +191 -47
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/routes/meta.py +8 -1
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/routes/serving.py +106 -37
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/pyproject.toml +11 -8
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_allocator.py +10 -0
- freesolo_flash_dev-1.0.4/tests/test_async_uploads.py +448 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_catalog_consistency.py +22 -2
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_checkpoints.py +15 -14
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_cli_commands.py +90 -9
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_cli_commands_coverage.py +3 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_cli_render_coverage.py +8 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_cli_render_theme.py +108 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_client.py +58 -6
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_cost_models.py +2 -2
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_disk_gb.py +5 -9
- freesolo_flash_dev-1.0.4/tests/test_env_push.py +700 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_flash_mvp.py +5 -1
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_gpus.py +2 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_grpo_params.py +60 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_health_liveness.py +6 -1
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_jobs.py +492 -44
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_kernel_fingerprint.py +30 -3
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_lora_rank_preflight.py +15 -5
- freesolo_flash_dev-1.0.4/tests/test_multimodal_input_grads.py +75 -0
- freesolo_flash_dev-1.0.4/tests/test_multimodal_training.py +969 -0
- freesolo_flash_dev-1.0.4/tests/test_multiturn_image_rollout.py +291 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_multiturn_rollout.py +1 -1
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_multiturn_rollout_coverage.py +1 -1
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_multiturn_rollout_request_policy.py +4 -4
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_opd.py +923 -48
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_opd_vllm.py +46 -1
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_packing.py +66 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_provider_routing.py +17 -2
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_resolve_params_b.py +1 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_resume_on_retry.py +65 -24
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_runmgmt.py +422 -24
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_serve.py +28 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_server_api.py +159 -5
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_serving_context_preflight.py +1 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_serving_contract.py +3 -2
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_sft_gc_off.py +7 -1
- freesolo_flash_dev-1.0.4/tests/test_single_turn_multimodal_grpo.py +368 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_spec_and_validation.py +49 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_train_context_preflight.py +22 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_training_controls.py +2 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_verifiers.py +22 -2
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_weight_cache.py +14 -5
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_worker_image.py +17 -14
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/uv.lock +4 -4
- freesolo_flash_dev-1.0.2/tests/test_env_push.py +0 -353
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/.claude/skills/verify/SKILL.md +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/.dockerignore +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/.env.example +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/.github/workflows/auto-rebake.yml +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/.github/workflows/ci.yml +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/.github/workflows/main-source-guard.yml +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/.github/workflows/notify-tests-repo.yml +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/.github/workflows/publish-dev.yml +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/.github/workflows/publish-image.yml +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/.github/workflows/publish.yml +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/.github/workflows/version-parity.yml +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/.github/workflows/worker-image.yml +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/.gitignore +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/Dockerfile +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/LICENSE +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/README.md +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/build/kernel_cache/.gitignore +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/build/kernel_cache/.keep +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/docker/Dockerfile.kernelcache +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/docker/Dockerfile.kernelcache.relayer +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/docker/bake_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/docker/bake_pod_entry.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/docker/kernel_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/docker/make_rp_handler.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/_channel.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/_fileio.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/_logging.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/_update_check.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/adapter_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/cli/__main__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/cli/_tty.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/cli/env_setup.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/client/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/client/config.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/client/runtime_secrets.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/client/specs.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/cost/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/cost/analytical.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/cost/facts.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/cost/spec.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/cost/types.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/accounting.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/chalk_kernels.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/steps.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/vram.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/__main__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/_pkg.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/adapter.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/decoding.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/finalize.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/gpu_setup.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/heartbeat.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/kernel_warmup.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/lora.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/opd_gkd.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/perf/attn.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/perf/diagnostics.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/perf/lifecycle.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/perf/liger.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/perf/loraplus.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/rng.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/tokenizer_align.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker/wandb_log.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/engine/worker_entrypoint.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/envs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/envs/archive.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/envs/archive_policy.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/envs/base.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/envs/pull.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/envs/registry.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/lora_rank.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/opd_retry_contract.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/_auth.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/_deadline.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/_hf_artifacts.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/_http.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/_instance.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/_instance_bootstrap.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/_instance_provider.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/_poll.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/allocator.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/base.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/lambdalabs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/lambdalabs/api.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/lambdalabs/auth.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/lambdalabs/gpus.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/lambdalabs/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/lambdalabs/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/lambdalabs/preflight.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/lambdalabs/pricing.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/preflight.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/realized.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/runpod/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/runpod/api.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/runpod/auth.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/runpod/cost.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/runpod/gpus.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/runpod/jobs.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/runpod/keys.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/runpod/preflight.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/runpod/preload.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/runpod/pricing.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/runpod/slots.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/runpod/train/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/runpod/train/endpoints.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/vast/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/vast/api.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/vast/auth.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/vast/jobs/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/vast/jobs/builders.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/vast/preflight.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/providers/vast/pricing.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/py.typed +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/runner/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/runner/verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/schema/fields.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/serve/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/serve/deploy.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/serve/export.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/serve/urls.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/__main__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/_deps.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/_locks.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/app.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/auth.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/billing.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/checkpoints.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/db.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/environment_registry.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/envs.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/reconcile.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/routes/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/routes/envs.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/routes/runs.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/server/run_registry.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/flash/spec.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/infisical-entrypoint.sh +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/scripts/build_dev_dist.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/_helpers/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/_helpers/runner.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/_helpers/specs.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/_helpers/vast.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/conftest.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/fixtures/math_eval.jsonl +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/fixtures/math_train.jsonl +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/live/__init__.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/live/conftest.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/live/test_lambda_live.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/live/test_runpod_live.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/live/test_vast_live.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_accounting_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_agent_flash_cli_contract.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_algorithms.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_b200_rollout_opt.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_backend_jobspec_contract.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_base_model_provenance.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_billing_retry.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_cancel_remote.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_chalk_kernels.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_charge_pricing.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_cli_errors.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_cli_estimate.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_cli_help.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_cli_main_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_cli_managed.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_client_server_integration.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_config_overrides.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_controlled_experiment_repairs.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_cost_analytical.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_cost_equation.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_cost_estimate.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_cost_hardware.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_cost_rewards.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_dev_channel.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_endpoint_name.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_env_cache_evict.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_env_delete.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_env_download.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_env_download_api.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_env_no_install.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_env_publish.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_env_pull.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_env_pull_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_env_pull_loader_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_env_pull_managed_control_plane.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_env_rate_limit_resolve.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_export.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_flash_worker.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_flashinfer_cache_dirs.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_github_urlopen_retry.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_grpo_mask_aware.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_grpo_sleep_gate.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_hf_retry.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_idle_endpoint_reaper.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_instance_bootstrap_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_internal_client.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_kernel_cache.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_kv_util.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_lambda_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_lambda_runner.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_logging.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_login_perms.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_lora_rank_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_managed_hf_repo.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_metrics_schema_agent_contract.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_mig_guard.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_multiturn_rollout_records.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_oom_escalate_gpu.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_opd_full_state_resume.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_opd_resume_safety.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_open_model_policy.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_orchestrator_flash.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_packing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_poll_helpers.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_preflight.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_provider_preflight_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_provider_teardown_robustness.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_providers_symmetry.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_realized_cost.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_repo_cleanup.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_runpod_api_delete.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_runpod_key_fingerprint.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_runpod_key_waterfall.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_runpod_slots.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_server_app_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_server_billing.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_server_checkpoints_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_server_db.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_server_envs_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_server_locks_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_serving_pricing_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_serving_schema_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_sft_example_selection.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_sft_max_context.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_structured_outputs.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_thinking_config.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_update_check.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_vast_api.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_vast_api_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_vast_offers.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_vast_provider_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_vast_runner.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_verified_revisions.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_version.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_vl_warmstart_adapter_keys.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_wandb_log_coverage.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_wandb_naming.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_warmstart_cross_repo.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_worker_dryrun.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_worker_hardexit.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_worker_init_heartbeat.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_worker_stack.py +0 -0
- {freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/tests/test_worker_thinking.py +0 -0
{freesolo_flash_dev-1.0.2 → freesolo_flash_dev-1.0.4}/.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" ;;
|
|
@@ -55,7 +55,7 @@ RUN pip install --no-cache-dir \
|
|
|
55
55
|
"flashinfer-python==0.6.6" \
|
|
56
56
|
"bitsandbytes>=0.49" \
|
|
57
57
|
"datasets>=4.7,<6" \
|
|
58
|
-
"huggingface_hub>=0
|
|
58
|
+
"huggingface_hub>=1.2.0" \
|
|
59
59
|
"accelerate>=1.4" \
|
|
60
60
|
"wandb>=0.17" \
|
|
61
61
|
"flash-linear-attention @ git+https://github.com/fla-org/flash-linear-attention.git@f0e213dbd8b5fb90c3c7eca869ac1706d5377139" \
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: freesolo-flash-dev
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
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
|
|
@@ -20,7 +20,7 @@ Requires-Dist: datasets>=2.19; extra == 'dev'
|
|
|
20
20
|
Requires-Dist: fastapi; extra == 'dev'
|
|
21
21
|
Requires-Dist: freesolo>=0.2.54; extra == 'dev'
|
|
22
22
|
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
23
|
-
Requires-Dist: huggingface-hub>=0
|
|
23
|
+
Requires-Dist: huggingface-hub>=1.2.0; extra == 'dev'
|
|
24
24
|
Requires-Dist: jsonschema>=4.23; extra == 'dev'
|
|
25
25
|
Requires-Dist: mypy>=1.13.0; extra == 'dev'
|
|
26
26
|
Requires-Dist: pytest>=9.0.3; extra == 'dev'
|
|
@@ -35,7 +35,7 @@ Requires-Dist: accelerate>=1.4; extra == 'gpu'
|
|
|
35
35
|
Requires-Dist: bitsandbytes>=0.49; extra == 'gpu'
|
|
36
36
|
Requires-Dist: datasets>=2.19; extra == 'gpu'
|
|
37
37
|
Requires-Dist: freesolo>=0.2.54; extra == 'gpu'
|
|
38
|
-
Requires-Dist: huggingface-hub>=0
|
|
38
|
+
Requires-Dist: huggingface-hub>=1.2.0; extra == 'gpu'
|
|
39
39
|
Requires-Dist: peft>=0.19; extra == 'gpu'
|
|
40
40
|
Requires-Dist: torch==2.10.0; extra == 'gpu'
|
|
41
41
|
Requires-Dist: transformers<5.11,>=5.6; extra == 'gpu'
|
|
@@ -46,7 +46,7 @@ Requires-Dist: datasets>=2.19; extra == 'server'
|
|
|
46
46
|
Requires-Dist: fastapi; extra == 'server'
|
|
47
47
|
Requires-Dist: freesolo>=0.2.54; extra == 'server'
|
|
48
48
|
Requires-Dist: httpx>=0.27; extra == 'server'
|
|
49
|
-
Requires-Dist: huggingface-hub>=0
|
|
49
|
+
Requires-Dist: huggingface-hub>=1.2.0; extra == 'server'
|
|
50
50
|
Requires-Dist: jsonschema>=4.23; extra == 'server'
|
|
51
51
|
Requires-Dist: referencing>=0.28.4; extra == 'server'
|
|
52
52
|
Requires-Dist: regex>=2024.11.6; extra == 'server'
|
|
@@ -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)",
|
|
@@ -424,7 +424,11 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
424
424
|
deployments.set_defaults(func=cmd_deployments)
|
|
425
425
|
|
|
426
426
|
chat = sub.add_parser("chat", help="chat with a deployed adapter")
|
|
427
|
-
chat.add_argument(
|
|
427
|
+
chat.add_argument(
|
|
428
|
+
"run_id",
|
|
429
|
+
help="a bare RUN_ID (its current deployment), RUN_ID/step-N (a specific deployed "
|
|
430
|
+
"checkpoint), or a full immutable adapter revision",
|
|
431
|
+
)
|
|
428
432
|
chat.add_argument("-m", "--message", required=True)
|
|
429
433
|
chat.add_argument(
|
|
430
434
|
"--system",
|
|
@@ -160,8 +160,9 @@ def cmd_gpus(args) -> int:
|
|
|
160
160
|
(info for info in GPU_INFO.values() if info.enum_member), key=lambda g: g.hourly_usd
|
|
161
161
|
)
|
|
162
162
|
tip = (
|
|
163
|
-
"Tip: GPU
|
|
164
|
-
"cheapest validated
|
|
163
|
+
"Tip: GPU allocation is automatic by default.\n"
|
|
164
|
+
"The allocator picks the cheapest validated class that fits. Pin a specific class by "
|
|
165
|
+
'adding exact_type = "<CLASS>" to the [gpu] section.'
|
|
165
166
|
)
|
|
166
167
|
if render.styled():
|
|
167
168
|
rows = [(info.name, info.vram_gb, runpod_rates.get(info.name)) for info in infos]
|
|
@@ -313,9 +314,8 @@ def cmd_train(args) -> int:
|
|
|
313
314
|
runtime_secrets_from_local_env(args.config, keys=spec.environment.secrets) or None
|
|
314
315
|
)
|
|
315
316
|
if args.dry_run:
|
|
316
|
-
# dry-run
|
|
317
|
-
#
|
|
318
|
-
# and charges nothing. a rejection surfaces as the server's error with exit status 1.
|
|
317
|
+
# dry-run runs submit-time server preflights without importing user code, allocating a gpu,
|
|
318
|
+
# or charging anything. a rejection surfaces as the server's error with exit status 1.
|
|
319
319
|
try:
|
|
320
320
|
status = client.create_run(
|
|
321
321
|
payload,
|
|
@@ -330,6 +330,14 @@ def cmd_train(args) -> int:
|
|
|
330
330
|
raise ApiError(exc.status, detail) from exc
|
|
331
331
|
compatibility = status.pop("train_schema_compatibility", None)
|
|
332
332
|
_print_train_schema_compatibility(compatibility)
|
|
333
|
+
print(
|
|
334
|
+
"dry-run validated: config/schema, model+algorithm compatibility, lora rank, "
|
|
335
|
+
"runtime-secret presence, warm-start source, serving context cap, and cost. it did NOT "
|
|
336
|
+
"import or run your environment.py; dataset loading, start_episode/episode shapes, "
|
|
337
|
+
"reward/scorer, worker imports, model load, and gpu/training are first exercised on the "
|
|
338
|
+
"worker after cold-start.",
|
|
339
|
+
file=sys.stderr,
|
|
340
|
+
)
|
|
333
341
|
if render.styled():
|
|
334
342
|
print(
|
|
335
343
|
render.object_panel(
|
|
@@ -376,18 +384,26 @@ def _log_follow_progress(status: dict | None, fallback_state: str) -> tuple[str,
|
|
|
376
384
|
parts = [state]
|
|
377
385
|
heartbeat = status.get("last_heartbeat") if isinstance(status, dict) else None
|
|
378
386
|
if isinstance(heartbeat, dict):
|
|
387
|
+
heartbeat_age_seconds = render._heartbeat_age_seconds(heartbeat.get("ts"))
|
|
379
388
|
stage = heartbeat.get("stage")
|
|
380
389
|
if stage:
|
|
381
390
|
parts.append(f"stage={stage}")
|
|
391
|
+
if state == "running":
|
|
392
|
+
warmup = render.warmup_message(
|
|
393
|
+
stage,
|
|
394
|
+
heartbeat_age_seconds,
|
|
395
|
+
render.heartbeat_is_current_attempt(status, heartbeat),
|
|
396
|
+
)
|
|
397
|
+
if warmup:
|
|
398
|
+
parts.append(warmup)
|
|
382
399
|
step = heartbeat.get("step")
|
|
383
400
|
if step is not None:
|
|
384
401
|
parts.append(f"step={step}")
|
|
385
402
|
# live heartbeat age so a long quiet phase reads as "alive, throttled" not "frozen".
|
|
386
403
|
# minute granularity: the non-TTY follow path prints a line whenever this string changes,
|
|
387
404
|
# so a seconds-precision age would emit one line per poll.
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
mins = int(max(0.0, time.time() - ts) // 60)
|
|
405
|
+
if heartbeat_age_seconds is not None:
|
|
406
|
+
mins = int(heartbeat_age_seconds // 60)
|
|
391
407
|
parts.append(f"hb={mins}m" if mins else "hb=<1m")
|
|
392
408
|
realized = status.get("realized_cost_usd")
|
|
393
409
|
if realized is not None:
|
|
@@ -700,7 +716,7 @@ def cmd_deployments(args) -> int:
|
|
|
700
716
|
return 0
|
|
701
717
|
print(
|
|
702
718
|
f"{'RUN ID':<30} {'STEP':<6} {'REVISION':<40} {'STATE':<14} "
|
|
703
|
-
f"{'VERIFIED AT':<
|
|
719
|
+
f"{'VERIFIED AT':<20} {'OPENAI MODEL':<30} DETAIL"
|
|
704
720
|
)
|
|
705
721
|
for row in rows:
|
|
706
722
|
deployment = row.get("deployment") or {}
|
|
@@ -708,14 +724,16 @@ def cmd_deployments(args) -> int:
|
|
|
708
724
|
step = deployment.get("checkpoint_step")
|
|
709
725
|
step_text = "final" if step is None else str(step)
|
|
710
726
|
verified_at = deployment.get("verified_at")
|
|
711
|
-
verified_text =
|
|
727
|
+
verified_text = (
|
|
728
|
+
"-" if verified_at is None else (render._humanize_ts(verified_at) or str(verified_at))
|
|
729
|
+
)
|
|
712
730
|
revision = str(deployment.get("adapter_revision") or "-")
|
|
713
731
|
state = str(deployment.get("state") or "-")
|
|
714
732
|
openai_model = str(deployment.get("openai_model") or run_id)
|
|
715
733
|
detail = str(deployment.get("error") or deployment.get("detail") or "")[:160]
|
|
716
734
|
print(
|
|
717
735
|
f"{run_id:<30} {step_text:<6} {revision:<40} {state:<14} "
|
|
718
|
-
f"{verified_text:<
|
|
736
|
+
f"{verified_text:<20} {openai_model:<30} {detail}"
|
|
719
737
|
)
|
|
720
738
|
return 0
|
|
721
739
|
|
|
@@ -728,18 +746,11 @@ def cmd_chat(args) -> int:
|
|
|
728
746
|
if revision is None and parsed is None:
|
|
729
747
|
print(
|
|
730
748
|
f"invalid chat target {args.run_id!r} "
|
|
731
|
-
"(expected a bare <run_id
|
|
732
|
-
file=sys.stderr,
|
|
733
|
-
)
|
|
734
|
-
return 1
|
|
735
|
-
if revision is None and parsed[1] is not None:
|
|
736
|
-
print(
|
|
737
|
-
"RUN_ID/step-N is not a valid chat target because it would route through the mutable "
|
|
738
|
-
"run alias; use the full immutable adapter revision returned by `flash deployments`",
|
|
749
|
+
"(expected a bare <run_id>, <run_id>/step-N, or full immutable adapter revision)",
|
|
739
750
|
file=sys.stderr,
|
|
740
751
|
)
|
|
741
752
|
return 1
|
|
742
|
-
chat_target = args.run_id
|
|
753
|
+
chat_target = args.run_id
|
|
743
754
|
client = client_from_config()
|
|
744
755
|
messages = [{"role": "user", "content": args.message}]
|
|
745
756
|
system = getattr(args, "system", None)
|
|
@@ -11,6 +11,7 @@ import os
|
|
|
11
11
|
import re
|
|
12
12
|
import sys
|
|
13
13
|
import tempfile
|
|
14
|
+
from collections.abc import Iterator
|
|
14
15
|
from pathlib import Path
|
|
15
16
|
from typing import TYPE_CHECKING, Literal, TypeAlias
|
|
16
17
|
|
|
@@ -174,6 +175,10 @@ def cmd_env_delete(args) -> int:
|
|
|
174
175
|
|
|
175
176
|
|
|
176
177
|
_ENV_ENTRYPOINT = "environment.py"
|
|
178
|
+
_ENV_SYSPATH_BOOTSTRAP = (
|
|
179
|
+
"import os as _flash_os, sys as _flash_sys\n"
|
|
180
|
+
"_flash_sys.path.insert(0, _flash_os.path.dirname(__file__))\n"
|
|
181
|
+
)
|
|
177
182
|
_ENV_PUSH_IGNORED_NAMES = frozenset(
|
|
178
183
|
{
|
|
179
184
|
".prime",
|
|
@@ -183,26 +188,47 @@ _ENV_PUSH_IGNORED_NAMES = frozenset(
|
|
|
183
188
|
".venv",
|
|
184
189
|
".mypy_cache",
|
|
185
190
|
".pytest_cache",
|
|
186
|
-
"
|
|
187
|
-
"
|
|
191
|
+
".ruff_cache",
|
|
192
|
+
".idea",
|
|
193
|
+
".vscode",
|
|
194
|
+
".ds_store",
|
|
195
|
+
".git-worktrees",
|
|
196
|
+
".env",
|
|
197
|
+
".netrc",
|
|
198
|
+
".aws",
|
|
199
|
+
".ssh",
|
|
200
|
+
"venv",
|
|
201
|
+
"node_modules",
|
|
202
|
+
"credentials",
|
|
203
|
+
"secrets",
|
|
188
204
|
}
|
|
189
205
|
)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
".yml",
|
|
204
|
-
}
|
|
206
|
+
_ENV_PUSH_ROOT_IGNORED_NAMES = frozenset({"pyproject.toml", "source"})
|
|
207
|
+
_ENV_PUSH_SECRET_PATTERNS = (
|
|
208
|
+
".env.*",
|
|
209
|
+
"*.env",
|
|
210
|
+
"*.pem",
|
|
211
|
+
"*.key",
|
|
212
|
+
"*.pfx",
|
|
213
|
+
"*.p12",
|
|
214
|
+
"credentials*",
|
|
215
|
+
"id_rsa*",
|
|
216
|
+
"id_dsa*",
|
|
217
|
+
"id_ecdsa*",
|
|
218
|
+
"id_ed25519*",
|
|
205
219
|
)
|
|
220
|
+
# secret patterns match secret data FILES only (see _ignore_env_push_path): they never prune a
|
|
221
|
+
# directory, so a package tree like credentials_store/ still ships, and python source is exempt,
|
|
222
|
+
# so helper modules like credentials.py or id_ed25519_loader.py travel instead of being silently
|
|
223
|
+
# dropped and breaking the worker with ModuleNotFoundError. that keeps the prefixes broad enough
|
|
224
|
+
# to catch backup variants (credentials_backup, id_rsa_backup) without eating code or packages.
|
|
225
|
+
_ENV_PUSH_CODE_SUFFIXES = frozenset({".py", ".pyi"})
|
|
226
|
+
_ENV_PUSH_MAX_TOTAL_BYTES = 256 * 1024 * 1024
|
|
227
|
+
|
|
228
|
+
try:
|
|
229
|
+
from flash.envs.archive_policy import ARCHIVE_MEMBER_LIMIT as _ENV_PUSH_MAX_FILES
|
|
230
|
+
except ImportError:
|
|
231
|
+
_ENV_PUSH_MAX_FILES = 5000
|
|
206
232
|
|
|
207
233
|
|
|
208
234
|
def _normalize_env_name(raw: str) -> str | None:
|
|
@@ -227,16 +253,12 @@ def _normalize_env_name(raw: str) -> str | None:
|
|
|
227
253
|
|
|
228
254
|
def _with_syspath_bootstrap(env_source: str) -> str:
|
|
229
255
|
"""Prepend a sys.path bootstrap so a published env can resolve shipped sibling helpers."""
|
|
230
|
-
bootstrap = (
|
|
231
|
-
"import os as _flash_os, sys as _flash_sys\n"
|
|
232
|
-
"_flash_sys.path.insert(0, _flash_os.path.dirname(__file__))\n"
|
|
233
|
-
)
|
|
234
256
|
import ast
|
|
235
257
|
|
|
236
258
|
try:
|
|
237
259
|
tree = ast.parse(env_source)
|
|
238
260
|
except SyntaxError:
|
|
239
|
-
return
|
|
261
|
+
return _ENV_SYSPATH_BOOTSTRAP + env_source
|
|
240
262
|
insert_after = 0
|
|
241
263
|
body = tree.body
|
|
242
264
|
i = 0
|
|
@@ -252,7 +274,16 @@ def _with_syspath_bootstrap(env_source: str) -> str:
|
|
|
252
274
|
insert_after = body[i].end_lineno or insert_after
|
|
253
275
|
i += 1
|
|
254
276
|
lines = env_source.splitlines(keepends=True)
|
|
255
|
-
return
|
|
277
|
+
return (
|
|
278
|
+
"".join(lines[:insert_after])
|
|
279
|
+
+ _ENV_SYSPATH_BOOTSTRAP
|
|
280
|
+
+ "".join(lines[insert_after:])
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _raise_walk_error(error: OSError) -> None:
|
|
285
|
+
"""fail env packaging loudly instead of silently skipping an unreadable directory."""
|
|
286
|
+
raise error
|
|
256
287
|
|
|
257
288
|
|
|
258
289
|
def _tar_b64(directory: Path) -> str:
|
|
@@ -266,44 +297,130 @@ def _tar_b64(directory: Path) -> str:
|
|
|
266
297
|
with tarfile.open(fileobj=buf, mode="w:gz") as tar:
|
|
267
298
|
for root, dirs, files in os.walk(directory):
|
|
268
299
|
root_path = Path(root)
|
|
269
|
-
dirs[:] = sorted(d for d in dirs if d not in _ENV_PUSH_IGNORED_NAMES)
|
|
270
|
-
for name in dirs:
|
|
271
|
-
child = root_path / name
|
|
272
|
-
tar.add(child, arcname=str(child.relative_to(directory)), recursive=False)
|
|
300
|
+
dirs[:] = sorted(d for d in dirs if d.lower() not in _ENV_PUSH_IGNORED_NAMES)
|
|
273
301
|
for name in sorted(files):
|
|
274
302
|
child = root_path / name
|
|
275
303
|
tar.add(child, arcname=str(child.relative_to(directory)), recursive=False)
|
|
276
304
|
return base64.b64encode(buf.getvalue()).decode()
|
|
277
305
|
|
|
278
306
|
|
|
279
|
-
def
|
|
280
|
-
"""
|
|
281
|
-
import
|
|
307
|
+
def _ignore_env_push_path(path: Path, *, env_root: Path, entrypoint: Path) -> bool:
|
|
308
|
+
"""Return whether a source path must not be included in an env package."""
|
|
309
|
+
import fnmatch
|
|
310
|
+
|
|
311
|
+
name = path.name
|
|
312
|
+
lowered_name = name.lower()
|
|
313
|
+
if path.is_symlink():
|
|
314
|
+
return True
|
|
315
|
+
if path.is_dir() and (path / "pyvenv.cfg").is_file():
|
|
316
|
+
return True
|
|
317
|
+
if path == entrypoint or (
|
|
318
|
+
path.parent == env_root and lowered_name == _ENV_ENTRYPOINT.lower()
|
|
319
|
+
):
|
|
320
|
+
return True
|
|
321
|
+
if lowered_name.startswith(".") or lowered_name in _ENV_PUSH_IGNORED_NAMES:
|
|
322
|
+
return True
|
|
323
|
+
if path.parent == env_root and lowered_name in _ENV_PUSH_ROOT_IGNORED_NAMES:
|
|
324
|
+
return True
|
|
325
|
+
if (
|
|
326
|
+
path.is_file()
|
|
327
|
+
and path.suffix.lower() not in _ENV_PUSH_CODE_SUFFIXES
|
|
328
|
+
and any(fnmatch.fnmatchcase(lowered_name, pattern) for pattern in _ENV_PUSH_SECRET_PATTERNS)
|
|
329
|
+
):
|
|
330
|
+
return True
|
|
331
|
+
return not (path.is_dir() or path.is_file())
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def _iter_env_sidecar_files(
|
|
335
|
+
env_root: Path, *, entrypoint: Path, include_full_tree: bool
|
|
336
|
+
) -> Iterator[tuple[Path, Path]]:
|
|
337
|
+
"""Yield publishable sidecar files and their package-relative paths."""
|
|
338
|
+
import os
|
|
282
339
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
340
|
+
roots = [env_root]
|
|
341
|
+
if not include_full_tree:
|
|
342
|
+
# a single-file push carries only the entrypoint, its dataset sidecars, and its own
|
|
343
|
+
# docs. ship user-authored docs so the synthesized stub readme does not stand in for
|
|
344
|
+
# real training guidance the user shipped next to the module.
|
|
345
|
+
for doc_name in ("README.md", "TRAINING.md"):
|
|
346
|
+
doc = env_root / doc_name
|
|
347
|
+
if doc.is_file() and not _ignore_env_push_path(
|
|
348
|
+
doc, env_root=env_root, entrypoint=entrypoint
|
|
349
|
+
):
|
|
350
|
+
yield doc, doc.relative_to(env_root)
|
|
351
|
+
roots = [
|
|
352
|
+
env_root / name
|
|
353
|
+
for name in ("dataset", "datasets")
|
|
354
|
+
if (env_root / name).is_dir() and not (env_root / name).is_symlink()
|
|
355
|
+
]
|
|
356
|
+
|
|
357
|
+
for walk_root in roots:
|
|
358
|
+
for root, dirs, files in os.walk(
|
|
359
|
+
walk_root, topdown=True, followlinks=False, onerror=_raise_walk_error
|
|
288
360
|
):
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
shutil.copytree(
|
|
296
|
-
child,
|
|
297
|
-
target,
|
|
298
|
-
ignore=shutil.ignore_patterns(*_ENV_PUSH_IGNORED_NAMES),
|
|
361
|
+
root_path = Path(root)
|
|
362
|
+
dirs[:] = sorted(
|
|
363
|
+
name
|
|
364
|
+
for name in dirs
|
|
365
|
+
if not _ignore_env_push_path(
|
|
366
|
+
root_path / name, env_root=env_root, entrypoint=entrypoint
|
|
299
367
|
)
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
368
|
+
)
|
|
369
|
+
for name in sorted(files):
|
|
370
|
+
child = root_path / name
|
|
371
|
+
if _ignore_env_push_path(child, env_root=env_root, entrypoint=entrypoint):
|
|
372
|
+
continue
|
|
373
|
+
yield child, child.relative_to(env_root)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def _check_env_push_limits(
|
|
377
|
+
env_root: Path, *, entrypoint: Path, include_full_tree: bool, env_name: str
|
|
378
|
+
) -> None:
|
|
379
|
+
"""Reject oversized source trees before copying or building an in-memory archive."""
|
|
380
|
+
files = 1
|
|
381
|
+
total_bytes = entrypoint.stat().st_size + len(_ENV_SYSPATH_BOOTSTRAP.encode())
|
|
382
|
+
directories: set[Path] = set()
|
|
383
|
+
has_readme = False
|
|
384
|
+
for child, relative in _iter_env_sidecar_files(
|
|
385
|
+
env_root, entrypoint=entrypoint, include_full_tree=include_full_tree
|
|
386
|
+
):
|
|
387
|
+
files += 1
|
|
388
|
+
total_bytes += child.stat().st_size
|
|
389
|
+
has_readme = has_readme or relative == Path("README.md")
|
|
390
|
+
# the server counts every file AND directory against the same member cap when it
|
|
391
|
+
# repackages the checkout for download, so count unique parent dirs here too.
|
|
392
|
+
directories.update(parent for parent in relative.parents if parent != Path("."))
|
|
393
|
+
if not has_readme:
|
|
394
|
+
files += 1
|
|
395
|
+
total_bytes += len(f"# {env_name}\n\nFlash Freesolo environment.\n".encode())
|
|
396
|
+
|
|
397
|
+
members = files + len(directories)
|
|
398
|
+
if total_bytes > _ENV_PUSH_MAX_TOTAL_BYTES:
|
|
399
|
+
raise ValueError(
|
|
400
|
+
f"environment package totals {_human_bytes(total_bytes)} "
|
|
401
|
+
f"(limit {_human_bytes(_ENV_PUSH_MAX_TOTAL_BYTES)}); "
|
|
402
|
+
"remove large artifacts or use a smaller dataset"
|
|
403
|
+
)
|
|
404
|
+
if members > _ENV_PUSH_MAX_FILES:
|
|
405
|
+
raise ValueError(
|
|
406
|
+
f"selected environment contains {members:,} files and directories "
|
|
407
|
+
f"(limit {_ENV_PUSH_MAX_FILES:,}); "
|
|
408
|
+
"remove large artifacts or use a smaller dataset"
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def _copy_env_sidecars(
|
|
413
|
+
env_root: Path, dest: Path, *, entrypoint: Path, include_full_tree: bool
|
|
414
|
+
) -> None:
|
|
415
|
+
"""Copy publishable sidecar files beside environment.py, creating directories lazily."""
|
|
416
|
+
import shutil
|
|
417
|
+
|
|
418
|
+
for child, relative in _iter_env_sidecar_files(
|
|
419
|
+
env_root, entrypoint=entrypoint, include_full_tree=include_full_tree
|
|
420
|
+
):
|
|
421
|
+
target = dest / relative
|
|
422
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
423
|
+
shutil.copy2(child, target)
|
|
307
424
|
|
|
308
425
|
|
|
309
426
|
def _human_bytes(n: int) -> str:
|
|
@@ -385,8 +502,11 @@ def cmd_env_push(args) -> int:
|
|
|
385
502
|
src = Path(args.path)
|
|
386
503
|
if not src.exists():
|
|
387
504
|
return _err(f"no such path: {src}")
|
|
505
|
+
if src.is_symlink():
|
|
506
|
+
return _err(f"cannot publish {src}: symlinks are not allowed")
|
|
388
507
|
|
|
389
|
-
|
|
508
|
+
include_full_tree = src.is_dir()
|
|
509
|
+
if include_full_tree:
|
|
390
510
|
canonical_entrypoint = src / _ENV_ENTRYPOINT
|
|
391
511
|
if canonical_entrypoint.is_file():
|
|
392
512
|
entrypoint = canonical_entrypoint
|
|
@@ -410,11 +530,29 @@ def cmd_env_push(args) -> int:
|
|
|
410
530
|
else:
|
|
411
531
|
return _err(f"cannot publish {src}: expected a Freesolo .py module or an env directory.")
|
|
412
532
|
|
|
533
|
+
if entrypoint.is_symlink():
|
|
534
|
+
return _err(f"cannot publish {entrypoint}: symlinks are not allowed")
|
|
535
|
+
|
|
536
|
+
try:
|
|
537
|
+
_check_env_push_limits(
|
|
538
|
+
env_root,
|
|
539
|
+
entrypoint=entrypoint,
|
|
540
|
+
include_full_tree=include_full_tree,
|
|
541
|
+
env_name=env_name,
|
|
542
|
+
)
|
|
543
|
+
except (OSError, ValueError) as exc:
|
|
544
|
+
return _err(f"cannot publish {src}: {exc}")
|
|
545
|
+
|
|
413
546
|
with tempfile.TemporaryDirectory(prefix="flash-env-push-") as tmp:
|
|
414
547
|
pkg = Path(tmp)
|
|
415
548
|
module_source = entrypoint.read_text()
|
|
416
549
|
(pkg / _ENV_ENTRYPOINT).write_text(_with_syspath_bootstrap(module_source))
|
|
417
|
-
_copy_env_sidecars(
|
|
550
|
+
_copy_env_sidecars(
|
|
551
|
+
env_root,
|
|
552
|
+
pkg,
|
|
553
|
+
entrypoint=entrypoint,
|
|
554
|
+
include_full_tree=include_full_tree,
|
|
555
|
+
)
|
|
418
556
|
# Only synthesize a stub README when the env didn't ship its own (now carried as a
|
|
419
557
|
# ``.md`` sidecar) — don't clobber a user-authored README with boilerplate.
|
|
420
558
|
readme = pkg / "README.md"
|