diff --git a/.agents/skills/fine-tuning/SKILL.md b/.agents/skills/fine-tuning/SKILL.md index 9d6591e9..73c91863 100644 --- a/.agents/skills/fine-tuning/SKILL.md +++ b/.agents/skills/fine-tuning/SKILL.md @@ -56,6 +56,8 @@ Train language models with SFT, KTO, and GRPO locally or on supported cloud prov | Prepare verified raw-text SFT data | `python tuner.py prepare-dataset --config --json` | | Check SFT loss masking (tokenizer only) | `python tuner.py doctor sft-mask --dataset-path --model ` | | Inspect native training host candidate | `python tuner.py doctor native-training --json` — read-only platform/Python/package metadata; see `reference/native-local-training.md` | +| Diagnose an offline SFT worker rejection | Read the exact retained `trainer.stderr.log`; closed phase/category/milestone labels locate a boundary, not a cause or retry authority; see `reference/sft-training.md` | +| Native MLX prepared SFT candidate | Internal exact-token batch/loss and runtime comparison only; no native launch CLI or API provider yet; see `reference/native-local-training.md` | | Plan a derived training image | `python scripts/qualify_derived_training_image.py plan --config Trainers/image_profiles/.yaml` | | Warm Space scaffold | `python3 Trainers/cloud/scripts/manage_space.py render --template vllm_warm --output-dir /tmp/my-space --base-image ghcr.io//:` | | Warm Space deploy | `python3 Trainers/cloud/scripts/manage_space.py deploy --space-id / --template vllm_warm --base-image ghcr.io//: --hardware a10g-small --sleep-time 3600 --var BASE_MODEL=` | diff --git a/.agents/skills/fine-tuning/configs/qwen35_4b_token_profile.yaml b/.agents/skills/fine-tuning/configs/qwen35_4b_token_profile.yaml index 7eb1561f..ca01872f 100644 --- a/.agents/skills/fine-tuning/configs/qwen35_4b_token_profile.yaml +++ b/.agents/skills/fine-tuning/configs/qwen35_4b_token_profile.yaml @@ -8,7 +8,7 @@ model: runtime: provider: modal lock_path: tuner/execution/providers/modal/modal-runtime-v1.lock.json - expected_lock_sha256: 42c25ecab7848fd5002bc27711e342d1b8fac0761621af65cba3736ad9110727 + expected_lock_sha256: 4f3fa81c9aeb9130258dec23f2b841e6dbdf2d686d1cf6b50249e743c3d496ca input: jsonl_path: /replace/with/private/prepared-dataset.jsonl mode: text diff --git a/.agents/skills/fine-tuning/reference/grpo-training.md b/.agents/skills/fine-tuning/reference/grpo-training.md index 16d5bd96..fde169e8 100644 --- a/.agents/skills/fine-tuning/reference/grpo-training.md +++ b/.agents/skills/fine-tuning/reference/grpo-training.md @@ -198,6 +198,14 @@ end-to-end provider deadline or public cancellation qualification. The first executable candidate requires identical model and tokenizer revisions because both consume one verified local snapshot. +The packaged runtime resolves `GRPOConfig` precision before loading the model. +When the retained model dtype is `null`, it passes the effective FP16 or BF16 +dtype to Unsloth's loader. Pinned Unsloth checks that loaded dtype against +trainer precision during `GRPOTrainer` construction; allowing the loader to +auto-select BF16 on BF16-capable hardware while the recipe requests FP16 can +fail at `TRAINER_SETUP`. A closed `TypeError` marker alone cannot establish +that this was the historical cause, and it does not authorize replay. + GRPO has a separate fixed 131-member offline inventory and 13-member outer closure. Existing SFT inventories remain unchanged. Maintenance uses the checked-in command, offline first because the outer closure binds its bytes: diff --git a/.agents/skills/fine-tuning/reference/native-local-training.md b/.agents/skills/fine-tuning/reference/native-local-training.md index db643a77..3831ab8f 100644 --- a/.agents/skills/fine-tuning/reference/native-local-training.md +++ b/.agents/skills/fine-tuning/reference/native-local-training.md @@ -26,3 +26,41 @@ Resolve with the documented platform/interpreter/index and install only in a fresh private environment with hashes required and source builds disabled. This candidate does not select a model, enable a provider, or qualify Metal, SFT or GRPO. Keep CUDA/Modal environments and the legacy Mac checkout unchanged. + +## Prepared SFT worker candidate + +`tuner/runtime/native_mlx_sft.py` is an internal CPU-tested boundary. It consumes +the existing shared `PreparedSFTExample` objects after host verification and +tokenization; it does not introduce a dataset format or public execution API. +Pass the exact prepared-input identity, tokenizer revision, retained row IDs, +declared train/validation tuples and explicit `MLXSFTConfig` controls. The +projection copies all tokens/labels immutably and records a deterministic digest. +The host must still bind those rows to the authenticated source and tokenizer. + +No MLX text/chat loader, extra EOS, resplit, truncation, packing, tail dropping, +or single-offset masking is allowed. Failed masking diagnostics, already +truncated rows, attention masks other than all-ones unpadded causal rows, and +rows without shifted target supervision reject. Arbitrary ignored token spans +remain ignored. Training shuffles deterministically within its own split; +validation retains every row in declared order, including partial batches. + +The custom hooks target pinned MLX-LM 0.32.2. One batch is one optimizer update; +gradient accumulation and checkpointing are deliberately unavailable in this +increment. Upstream accumulation averages microbatch means, which needs a +separate objective review for unequal supervised-token counts. Model, LoRA, +optimizer and learning-rate policy must be prepared explicitly by the future +child composition; this worker does not infer or silently apply those controls. + +`tuner/runtime/native_mlx_runtime.py` compares host-supplied exact interpreter +and complete distribution pins against the running candidate before worker ML +imports. `CANDIDATE_MATCH_ONLY` means metadata and executable bytes match. +It does not authenticate wheel files, imported module origins, Homebrew's linked +Python framework, other native libraries, Metal, or launch authority. Retain the +original hash lock and use the existing `uv pip freeze`, `uv pip check`, +`shasum -a 256` and `otool -L` tools for review evidence; do not install packages +into the shared interpreter or treat a newly observed inventory as approved. + +There is no native launch CLI yet. The internal worker may run only in an +independently admitted, host-owned bounded child. The desktop plan lists the +missing native runtime material, child lifecycle, artifact roles and API joins. +Do not call the worker as an ad hoc substitute for that composition. diff --git a/.agents/skills/fine-tuning/reference/sft-training.md b/.agents/skills/fine-tuning/reference/sft-training.md index 062a8a7f..3029c594 100644 --- a/.agents/skills/fine-tuning/reference/sft-training.md +++ b/.agents/skills/fine-tuning/reference/sft-training.md @@ -460,3 +460,45 @@ The CPU `probe-filesystem` command measures actual native writer modes and host readability under a reviewed finite container plan; it is not training, GPU, provider or cancellation qualification. Source inventory and five-role native verification remain closed; no default capability or UID policy is promoted. + +Before short-lived source preparation, run `model-preflight` with the exact +interpreter selected for `prepare-model`; the base consumer may lack the optional +Hub SDK. This checks only the existing SDK signature contract and creates no +client or claim. `prepare-model` repeats it before its one-use claim. Use an +independently verified helper environment; do not upgrade the pinned trainer. +Create and verify necessary private source/artifact parent directories first. +Preserve any consumed model-preparation claim after failure. New failures retain +the existing allowlisted `model_preparation_stage`; never infer that stage or a +cause for an older generic diagnostic, or replay an uncertain attempt. + +### Offline SFT worker rejection + +The isolated source worker retains exit code 2 on failure and emits one bounded +diagnostic line to the existing `tracking/trainer.stderr.log`: +`OFFLINE_SFT_WORKER_REJECTED phase= category=`. +The phases are `BOOTSTRAP`, `CLOSURE`, `BOOTSTRAP_ORIGIN`, `ARGUMENTS`, +`IMPORT_GUARD`, `TRAINER`, and `POSTCHECK`. Categories are `REJECTION`, `IMPORT`, +`OS`, `VALUE`, `SYSTEM_EXIT`, and `OTHER`. During `TRAINER`, the last valid +existing packaged-trainer hook may append `milestone=` (for example +`TORCH_IMPORT` or `MODEL_LIBRARY_LOAD`). Unknown hooks clear that milestone. +The fallback remains the bare rejection marker. This diagnostic does not print +exception text, arguments, paths, environment values, or traceback frames; +library-generated stdout/stderr is a separate surface. + +Use the labels to choose the next targeted reproduction. An import category +does not identify the missing library, and a model-load milestone does not +establish OOM. Do not interpret an older bare marker retrospectively. Preserve +the consumed claim, roots, container and retained logs; the diagnostics never +authorize replay. See `docs/review/offline-sft-worker-diagnostics.md` for the +October 20 evidence boundary, CPU regressions and fresh-run prerequisites. + +After an intentional worker source change, check and refresh existing +manifests with the checked-in maintainers in dependency order: +`regenerate_offline_sft_worker_closure.py`, +`regenerate_packaged_training_worker_closure.py`, +`regenerate_grpo_worker_closures.py --kind offline`, then `--kind outer`, +`regenerate_hf_parent_closure.py`, `regenerate_modal_runtime_lock.py`, and +`regenerate_modal_inference_lock.py` (all under `scripts/`). Run each read-only +check first, review its drift, use `--write` only for intentional hash refresh, +then recheck. Preserve inventories and non-hash pins. Sync canonical skills +with `.skills/scripts/sync_skill_trees.py` and verify its `--check`. diff --git a/.claude/skills/fine-tuning/SKILL.md b/.claude/skills/fine-tuning/SKILL.md index 9d6591e9..73c91863 100644 --- a/.claude/skills/fine-tuning/SKILL.md +++ b/.claude/skills/fine-tuning/SKILL.md @@ -56,6 +56,8 @@ Train language models with SFT, KTO, and GRPO locally or on supported cloud prov | Prepare verified raw-text SFT data | `python tuner.py prepare-dataset --config --json` | | Check SFT loss masking (tokenizer only) | `python tuner.py doctor sft-mask --dataset-path --model ` | | Inspect native training host candidate | `python tuner.py doctor native-training --json` — read-only platform/Python/package metadata; see `reference/native-local-training.md` | +| Diagnose an offline SFT worker rejection | Read the exact retained `trainer.stderr.log`; closed phase/category/milestone labels locate a boundary, not a cause or retry authority; see `reference/sft-training.md` | +| Native MLX prepared SFT candidate | Internal exact-token batch/loss and runtime comparison only; no native launch CLI or API provider yet; see `reference/native-local-training.md` | | Plan a derived training image | `python scripts/qualify_derived_training_image.py plan --config Trainers/image_profiles/.yaml` | | Warm Space scaffold | `python3 Trainers/cloud/scripts/manage_space.py render --template vllm_warm --output-dir /tmp/my-space --base-image ghcr.io//:` | | Warm Space deploy | `python3 Trainers/cloud/scripts/manage_space.py deploy --space-id / --template vllm_warm --base-image ghcr.io//: --hardware a10g-small --sleep-time 3600 --var BASE_MODEL=` | diff --git a/.claude/skills/fine-tuning/configs/qwen35_4b_token_profile.yaml b/.claude/skills/fine-tuning/configs/qwen35_4b_token_profile.yaml index 7eb1561f..ca01872f 100644 --- a/.claude/skills/fine-tuning/configs/qwen35_4b_token_profile.yaml +++ b/.claude/skills/fine-tuning/configs/qwen35_4b_token_profile.yaml @@ -8,7 +8,7 @@ model: runtime: provider: modal lock_path: tuner/execution/providers/modal/modal-runtime-v1.lock.json - expected_lock_sha256: 42c25ecab7848fd5002bc27711e342d1b8fac0761621af65cba3736ad9110727 + expected_lock_sha256: 4f3fa81c9aeb9130258dec23f2b841e6dbdf2d686d1cf6b50249e743c3d496ca input: jsonl_path: /replace/with/private/prepared-dataset.jsonl mode: text diff --git a/.claude/skills/fine-tuning/reference/grpo-training.md b/.claude/skills/fine-tuning/reference/grpo-training.md index 16d5bd96..fde169e8 100644 --- a/.claude/skills/fine-tuning/reference/grpo-training.md +++ b/.claude/skills/fine-tuning/reference/grpo-training.md @@ -198,6 +198,14 @@ end-to-end provider deadline or public cancellation qualification. The first executable candidate requires identical model and tokenizer revisions because both consume one verified local snapshot. +The packaged runtime resolves `GRPOConfig` precision before loading the model. +When the retained model dtype is `null`, it passes the effective FP16 or BF16 +dtype to Unsloth's loader. Pinned Unsloth checks that loaded dtype against +trainer precision during `GRPOTrainer` construction; allowing the loader to +auto-select BF16 on BF16-capable hardware while the recipe requests FP16 can +fail at `TRAINER_SETUP`. A closed `TypeError` marker alone cannot establish +that this was the historical cause, and it does not authorize replay. + GRPO has a separate fixed 131-member offline inventory and 13-member outer closure. Existing SFT inventories remain unchanged. Maintenance uses the checked-in command, offline first because the outer closure binds its bytes: diff --git a/.claude/skills/fine-tuning/reference/native-local-training.md b/.claude/skills/fine-tuning/reference/native-local-training.md index db643a77..3831ab8f 100644 --- a/.claude/skills/fine-tuning/reference/native-local-training.md +++ b/.claude/skills/fine-tuning/reference/native-local-training.md @@ -26,3 +26,41 @@ Resolve with the documented platform/interpreter/index and install only in a fresh private environment with hashes required and source builds disabled. This candidate does not select a model, enable a provider, or qualify Metal, SFT or GRPO. Keep CUDA/Modal environments and the legacy Mac checkout unchanged. + +## Prepared SFT worker candidate + +`tuner/runtime/native_mlx_sft.py` is an internal CPU-tested boundary. It consumes +the existing shared `PreparedSFTExample` objects after host verification and +tokenization; it does not introduce a dataset format or public execution API. +Pass the exact prepared-input identity, tokenizer revision, retained row IDs, +declared train/validation tuples and explicit `MLXSFTConfig` controls. The +projection copies all tokens/labels immutably and records a deterministic digest. +The host must still bind those rows to the authenticated source and tokenizer. + +No MLX text/chat loader, extra EOS, resplit, truncation, packing, tail dropping, +or single-offset masking is allowed. Failed masking diagnostics, already +truncated rows, attention masks other than all-ones unpadded causal rows, and +rows without shifted target supervision reject. Arbitrary ignored token spans +remain ignored. Training shuffles deterministically within its own split; +validation retains every row in declared order, including partial batches. + +The custom hooks target pinned MLX-LM 0.32.2. One batch is one optimizer update; +gradient accumulation and checkpointing are deliberately unavailable in this +increment. Upstream accumulation averages microbatch means, which needs a +separate objective review for unequal supervised-token counts. Model, LoRA, +optimizer and learning-rate policy must be prepared explicitly by the future +child composition; this worker does not infer or silently apply those controls. + +`tuner/runtime/native_mlx_runtime.py` compares host-supplied exact interpreter +and complete distribution pins against the running candidate before worker ML +imports. `CANDIDATE_MATCH_ONLY` means metadata and executable bytes match. +It does not authenticate wheel files, imported module origins, Homebrew's linked +Python framework, other native libraries, Metal, or launch authority. Retain the +original hash lock and use the existing `uv pip freeze`, `uv pip check`, +`shasum -a 256` and `otool -L` tools for review evidence; do not install packages +into the shared interpreter or treat a newly observed inventory as approved. + +There is no native launch CLI yet. The internal worker may run only in an +independently admitted, host-owned bounded child. The desktop plan lists the +missing native runtime material, child lifecycle, artifact roles and API joins. +Do not call the worker as an ad hoc substitute for that composition. diff --git a/.claude/skills/fine-tuning/reference/sft-training.md b/.claude/skills/fine-tuning/reference/sft-training.md index 062a8a7f..3029c594 100644 --- a/.claude/skills/fine-tuning/reference/sft-training.md +++ b/.claude/skills/fine-tuning/reference/sft-training.md @@ -460,3 +460,45 @@ The CPU `probe-filesystem` command measures actual native writer modes and host readability under a reviewed finite container plan; it is not training, GPU, provider or cancellation qualification. Source inventory and five-role native verification remain closed; no default capability or UID policy is promoted. + +Before short-lived source preparation, run `model-preflight` with the exact +interpreter selected for `prepare-model`; the base consumer may lack the optional +Hub SDK. This checks only the existing SDK signature contract and creates no +client or claim. `prepare-model` repeats it before its one-use claim. Use an +independently verified helper environment; do not upgrade the pinned trainer. +Create and verify necessary private source/artifact parent directories first. +Preserve any consumed model-preparation claim after failure. New failures retain +the existing allowlisted `model_preparation_stage`; never infer that stage or a +cause for an older generic diagnostic, or replay an uncertain attempt. + +### Offline SFT worker rejection + +The isolated source worker retains exit code 2 on failure and emits one bounded +diagnostic line to the existing `tracking/trainer.stderr.log`: +`OFFLINE_SFT_WORKER_REJECTED phase= category=`. +The phases are `BOOTSTRAP`, `CLOSURE`, `BOOTSTRAP_ORIGIN`, `ARGUMENTS`, +`IMPORT_GUARD`, `TRAINER`, and `POSTCHECK`. Categories are `REJECTION`, `IMPORT`, +`OS`, `VALUE`, `SYSTEM_EXIT`, and `OTHER`. During `TRAINER`, the last valid +existing packaged-trainer hook may append `milestone=` (for example +`TORCH_IMPORT` or `MODEL_LIBRARY_LOAD`). Unknown hooks clear that milestone. +The fallback remains the bare rejection marker. This diagnostic does not print +exception text, arguments, paths, environment values, or traceback frames; +library-generated stdout/stderr is a separate surface. + +Use the labels to choose the next targeted reproduction. An import category +does not identify the missing library, and a model-load milestone does not +establish OOM. Do not interpret an older bare marker retrospectively. Preserve +the consumed claim, roots, container and retained logs; the diagnostics never +authorize replay. See `docs/review/offline-sft-worker-diagnostics.md` for the +October 20 evidence boundary, CPU regressions and fresh-run prerequisites. + +After an intentional worker source change, check and refresh existing +manifests with the checked-in maintainers in dependency order: +`regenerate_offline_sft_worker_closure.py`, +`regenerate_packaged_training_worker_closure.py`, +`regenerate_grpo_worker_closures.py --kind offline`, then `--kind outer`, +`regenerate_hf_parent_closure.py`, `regenerate_modal_runtime_lock.py`, and +`regenerate_modal_inference_lock.py` (all under `scripts/`). Run each read-only +check first, review its drift, use `--write` only for intentional hash refresh, +then recheck. Preserve inventories and non-hash pins. Sync canonical skills +with `.skills/scripts/sync_skill_trees.py` and verify its `--check`. diff --git a/.skills/fine-tuning/SKILL.md b/.skills/fine-tuning/SKILL.md index 9d6591e9..73c91863 100644 --- a/.skills/fine-tuning/SKILL.md +++ b/.skills/fine-tuning/SKILL.md @@ -56,6 +56,8 @@ Train language models with SFT, KTO, and GRPO locally or on supported cloud prov | Prepare verified raw-text SFT data | `python tuner.py prepare-dataset --config --json` | | Check SFT loss masking (tokenizer only) | `python tuner.py doctor sft-mask --dataset-path --model ` | | Inspect native training host candidate | `python tuner.py doctor native-training --json` — read-only platform/Python/package metadata; see `reference/native-local-training.md` | +| Diagnose an offline SFT worker rejection | Read the exact retained `trainer.stderr.log`; closed phase/category/milestone labels locate a boundary, not a cause or retry authority; see `reference/sft-training.md` | +| Native MLX prepared SFT candidate | Internal exact-token batch/loss and runtime comparison only; no native launch CLI or API provider yet; see `reference/native-local-training.md` | | Plan a derived training image | `python scripts/qualify_derived_training_image.py plan --config Trainers/image_profiles/.yaml` | | Warm Space scaffold | `python3 Trainers/cloud/scripts/manage_space.py render --template vllm_warm --output-dir /tmp/my-space --base-image ghcr.io//:` | | Warm Space deploy | `python3 Trainers/cloud/scripts/manage_space.py deploy --space-id / --template vllm_warm --base-image ghcr.io//: --hardware a10g-small --sleep-time 3600 --var BASE_MODEL=` | diff --git a/.skills/fine-tuning/configs/qwen35_4b_token_profile.yaml b/.skills/fine-tuning/configs/qwen35_4b_token_profile.yaml index 7eb1561f..ca01872f 100644 --- a/.skills/fine-tuning/configs/qwen35_4b_token_profile.yaml +++ b/.skills/fine-tuning/configs/qwen35_4b_token_profile.yaml @@ -8,7 +8,7 @@ model: runtime: provider: modal lock_path: tuner/execution/providers/modal/modal-runtime-v1.lock.json - expected_lock_sha256: 42c25ecab7848fd5002bc27711e342d1b8fac0761621af65cba3736ad9110727 + expected_lock_sha256: 4f3fa81c9aeb9130258dec23f2b841e6dbdf2d686d1cf6b50249e743c3d496ca input: jsonl_path: /replace/with/private/prepared-dataset.jsonl mode: text diff --git a/.skills/fine-tuning/reference/grpo-training.md b/.skills/fine-tuning/reference/grpo-training.md index 16d5bd96..fde169e8 100644 --- a/.skills/fine-tuning/reference/grpo-training.md +++ b/.skills/fine-tuning/reference/grpo-training.md @@ -198,6 +198,14 @@ end-to-end provider deadline or public cancellation qualification. The first executable candidate requires identical model and tokenizer revisions because both consume one verified local snapshot. +The packaged runtime resolves `GRPOConfig` precision before loading the model. +When the retained model dtype is `null`, it passes the effective FP16 or BF16 +dtype to Unsloth's loader. Pinned Unsloth checks that loaded dtype against +trainer precision during `GRPOTrainer` construction; allowing the loader to +auto-select BF16 on BF16-capable hardware while the recipe requests FP16 can +fail at `TRAINER_SETUP`. A closed `TypeError` marker alone cannot establish +that this was the historical cause, and it does not authorize replay. + GRPO has a separate fixed 131-member offline inventory and 13-member outer closure. Existing SFT inventories remain unchanged. Maintenance uses the checked-in command, offline first because the outer closure binds its bytes: diff --git a/.skills/fine-tuning/reference/native-local-training.md b/.skills/fine-tuning/reference/native-local-training.md index db643a77..3831ab8f 100644 --- a/.skills/fine-tuning/reference/native-local-training.md +++ b/.skills/fine-tuning/reference/native-local-training.md @@ -26,3 +26,41 @@ Resolve with the documented platform/interpreter/index and install only in a fresh private environment with hashes required and source builds disabled. This candidate does not select a model, enable a provider, or qualify Metal, SFT or GRPO. Keep CUDA/Modal environments and the legacy Mac checkout unchanged. + +## Prepared SFT worker candidate + +`tuner/runtime/native_mlx_sft.py` is an internal CPU-tested boundary. It consumes +the existing shared `PreparedSFTExample` objects after host verification and +tokenization; it does not introduce a dataset format or public execution API. +Pass the exact prepared-input identity, tokenizer revision, retained row IDs, +declared train/validation tuples and explicit `MLXSFTConfig` controls. The +projection copies all tokens/labels immutably and records a deterministic digest. +The host must still bind those rows to the authenticated source and tokenizer. + +No MLX text/chat loader, extra EOS, resplit, truncation, packing, tail dropping, +or single-offset masking is allowed. Failed masking diagnostics, already +truncated rows, attention masks other than all-ones unpadded causal rows, and +rows without shifted target supervision reject. Arbitrary ignored token spans +remain ignored. Training shuffles deterministically within its own split; +validation retains every row in declared order, including partial batches. + +The custom hooks target pinned MLX-LM 0.32.2. One batch is one optimizer update; +gradient accumulation and checkpointing are deliberately unavailable in this +increment. Upstream accumulation averages microbatch means, which needs a +separate objective review for unequal supervised-token counts. Model, LoRA, +optimizer and learning-rate policy must be prepared explicitly by the future +child composition; this worker does not infer or silently apply those controls. + +`tuner/runtime/native_mlx_runtime.py` compares host-supplied exact interpreter +and complete distribution pins against the running candidate before worker ML +imports. `CANDIDATE_MATCH_ONLY` means metadata and executable bytes match. +It does not authenticate wheel files, imported module origins, Homebrew's linked +Python framework, other native libraries, Metal, or launch authority. Retain the +original hash lock and use the existing `uv pip freeze`, `uv pip check`, +`shasum -a 256` and `otool -L` tools for review evidence; do not install packages +into the shared interpreter or treat a newly observed inventory as approved. + +There is no native launch CLI yet. The internal worker may run only in an +independently admitted, host-owned bounded child. The desktop plan lists the +missing native runtime material, child lifecycle, artifact roles and API joins. +Do not call the worker as an ad hoc substitute for that composition. diff --git a/.skills/fine-tuning/reference/sft-training.md b/.skills/fine-tuning/reference/sft-training.md index 062a8a7f..3029c594 100644 --- a/.skills/fine-tuning/reference/sft-training.md +++ b/.skills/fine-tuning/reference/sft-training.md @@ -460,3 +460,45 @@ The CPU `probe-filesystem` command measures actual native writer modes and host readability under a reviewed finite container plan; it is not training, GPU, provider or cancellation qualification. Source inventory and five-role native verification remain closed; no default capability or UID policy is promoted. + +Before short-lived source preparation, run `model-preflight` with the exact +interpreter selected for `prepare-model`; the base consumer may lack the optional +Hub SDK. This checks only the existing SDK signature contract and creates no +client or claim. `prepare-model` repeats it before its one-use claim. Use an +independently verified helper environment; do not upgrade the pinned trainer. +Create and verify necessary private source/artifact parent directories first. +Preserve any consumed model-preparation claim after failure. New failures retain +the existing allowlisted `model_preparation_stage`; never infer that stage or a +cause for an older generic diagnostic, or replay an uncertain attempt. + +### Offline SFT worker rejection + +The isolated source worker retains exit code 2 on failure and emits one bounded +diagnostic line to the existing `tracking/trainer.stderr.log`: +`OFFLINE_SFT_WORKER_REJECTED phase= category=`. +The phases are `BOOTSTRAP`, `CLOSURE`, `BOOTSTRAP_ORIGIN`, `ARGUMENTS`, +`IMPORT_GUARD`, `TRAINER`, and `POSTCHECK`. Categories are `REJECTION`, `IMPORT`, +`OS`, `VALUE`, `SYSTEM_EXIT`, and `OTHER`. During `TRAINER`, the last valid +existing packaged-trainer hook may append `milestone=` (for example +`TORCH_IMPORT` or `MODEL_LIBRARY_LOAD`). Unknown hooks clear that milestone. +The fallback remains the bare rejection marker. This diagnostic does not print +exception text, arguments, paths, environment values, or traceback frames; +library-generated stdout/stderr is a separate surface. + +Use the labels to choose the next targeted reproduction. An import category +does not identify the missing library, and a model-load milestone does not +establish OOM. Do not interpret an older bare marker retrospectively. Preserve +the consumed claim, roots, container and retained logs; the diagnostics never +authorize replay. See `docs/review/offline-sft-worker-diagnostics.md` for the +October 20 evidence boundary, CPU regressions and fresh-run prerequisites. + +After an intentional worker source change, check and refresh existing +manifests with the checked-in maintainers in dependency order: +`regenerate_offline_sft_worker_closure.py`, +`regenerate_packaged_training_worker_closure.py`, +`regenerate_grpo_worker_closures.py --kind offline`, then `--kind outer`, +`regenerate_hf_parent_closure.py`, `regenerate_modal_runtime_lock.py`, and +`regenerate_modal_inference_lock.py` (all under `scripts/`). Run each read-only +check first, review its drift, use `--write` only for intentional hash refresh, +then recheck. Preserve inventories and non-hash pins. Sync canonical skills +with `.skills/scripts/sync_skill_trees.py` and verify its `--check`. diff --git a/AGENTS.md b/AGENTS.md index 41bb1950..381fcb17 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,6 +14,7 @@ This repository has a few cloud-training constraints that are easy to relearn th - If the capability does not exist, the next step is not an ad hoc workaround. Update the relevant skill and add the proper checked-in script/CLI workflow so the new capability is reusable. - Prefer repo CLIs and checked-in scripts over manual bucket/API probing whenever those surfaces exist. - Diagnose failures from the exact attempt's retained records, logs, pinned SDK source, and provider documentation. Use relevant issue/forum reports to form hypotheses, then run a targeted experiment before calling a hypothesis the cause. A closed failure phase locates a boundary; it does not establish the cause. +- Offline SFT worker diagnostics may emit only closed phase/category/milestone labels, never exception text, paths, argv, environment values or traceback frames. Preserve the consumed attempt; a historical generic rejection cannot be assigned a later diagnostic stage. Refresh all affected SFT/GRPO closures and Modal training/inference locks before fresh qualification. - Keep the training workflow proportional to the supported use case. Remove redundant operations when documented behavior and experiments show they add no useful guarantee; retain source/model pins, credential isolation, artifact verification, and protection against replaying an uncertain submission. Test doubles must model the provider's documented behavior, not merely repeat our implementation. - For prompt/completion training, explicitly review the generation scaffold at the target boundary and keep model-specific chat-template arguments in recipe configuration, consistent with serving. Preserve legacy canonical documents when optional arguments are absent. An explicit null output budget means no request-level token ceiling, not unlimited context, time, or transport size. A completed-text smoke must check natural completion and must not be reported as a writing-quality evaluation. @@ -36,6 +37,21 @@ This repository has a few cloud-training constraints that are easy to relearn th - If a process requirement is important enough to affect how generation/eval work is done repeatedly, encode it in `AGENTS.md`, the canonical skill under `.skills/`, and the relevant checked-in config/docs before continuing. - For new unsteered local batch generation, prefer `batch-generate --engine vllm`. Set `VLLM_BATCH_INVARIANT=1` before process startup and pin the exact vLLM version, documented minimum CUDA compute capability, model and tokenizer revisions, dtype, tensor parallel size, scheduler limits, structured-output backend, model context limit, multimodal limits, decode settings, and JSON Schema when formatting is incidental. Keep generated rows and token artifacts in the consuming project's private output directory. +## Native MLX candidate + +- Native SFT consumes the shared preprocessor's exact token IDs and per-token + labels with host-declared split/row identities. Never route prepared rows + through MLX-LM text/chat loaders, a single-offset loss mask, implicit split, + truncation, or dropped tail batches. Reject unsupported attention layouts and + failed preprocessing diagnostics before ML imports. +- The internal `tuner/runtime/native_mlx_sft.py` worker and candidate metadata + comparison are not a native provider, public runtime material, or launch + authority. A finite iteration count is not a wall-clock/process bound. Keep + model/optimizer preparation, exact runtime/source admission, private paths, + durable claims, child lifecycle and native artifact verification host-owned. + Do not enable native training in Syntunia before those joins and real Mac + qualification are complete. See `docs/plans/native-desktop-training-api-v1.md`. + ## HF Jobs - For the protected paid A10G training smoke, use only `python tuner.py hf-training-smoke {preflight,approve,execute,recover,observe,verify}`: require the exact pushed-source, security/release, isolated CPython 3.12.7 launcher, live quote, and approval gates; allow one submission and one cancel attempt; keep credentials out of the remote job; and verify only the exact 15-file artifact inventory without bulk bucket sync. diff --git a/Trainers/grpo/runtime_v1.py b/Trainers/grpo/runtime_v1.py index 04c4a0f5..5370882d 100644 --- a/Trainers/grpo/runtime_v1.py +++ b/Trainers/grpo/runtime_v1.py @@ -468,10 +468,16 @@ def mark(value): args = build_grpo_config(config, output_root / 'checkpoints', config_class=deps['config_class'], bf16_supported=deps['bf16_supported']) helper = deps['model_loader'] + # Unsloth's patched GRPOTrainer checks the loaded model dtype against the + # effective GRPOConfig precision. Its loader otherwise chooses BF16 on a + # BF16-capable GPU even when the retained recipe requested FP16 training. + model_dtype = config['model'].get('dtype') + if model_dtype is None: + model_dtype = 'bfloat16' if args.bf16 else 'float16' if args.fp16 else None mark("MODEL_LIBRARY_LOAD") model, tokenizer, is_vl = helper.load_model_and_tokenizer( str(model_snapshot), max_seq_length=config['model']['max_seq_length'], - dtype=config['model'].get('dtype'), load_in_4bit=config['model'].get('load_in_4bit', True), + dtype=model_dtype, load_in_4bit=config['model'].get('load_in_4bit', True), offline=True) if is_vl: raise ValueError("retained GRPO requires text model") diff --git a/Trainers/mlx_sft_mac/runtime-candidate/README.md b/Trainers/mlx_sft_mac/runtime-candidate/README.md index 306ec549..cea4d936 100644 --- a/Trainers/mlx_sft_mac/runtime-candidate/README.md +++ b/Trainers/mlx_sft_mac/runtime-candidate/README.md @@ -86,7 +86,7 @@ Frozen inventory SHA-256: This is dependency-installation evidence only; no ML framework was imported and no Metal probe, model load or training step was executed. -## Worker boundary that still needs implementation +## Prepared worker boundary The pinned [dataset code](https://github.com/ml-explore/mlx-lm/blob/93e6d4a6447b3080861985947f0820e76fcb957d/mlx_lm/tuner/datasets.py) @@ -97,10 +97,35 @@ rows, chat-template options, or per-token target masks. The pinned [trainer](https://github.com/ml-explore/mlx-lm/blob/93e6d4a6447b3080861985947f0820e76fcb957d/mlx_lm/tuner/trainer.py) -accepts custom loss and batch iteration functions. The native worker should -consume the existing prepared splits and token semantics explicitly through -that boundary; it must not silently rerender, resplit or truncate targets. -Real token-boundary comparisons and parameter updates are still required. +accepts custom loss and batch iteration functions. The internal +`tuner/runtime/native_mlx_sft.py` candidate now implements those hooks over +shared `PreparedSFTExample` values, preserving split/row identities, arbitrary +ignored-label spans and partial batches without using the upstream loaders. +CPU contracts check causal shifting, safe ignored targets, token-weighted loss, +and complete validation traversal. Real tokenizer comparisons, MLX autograd and +parameter updates are still required. + +The associated `native_mlx_runtime.py` comparison accepts an exact host-supplied +inventory, interpreter hash and original dependency-lock hash. It rejects +metadata/interpreter drift before worker imports and returns only +`CANDIDATE_MATCH_ONLY`. It is neither an inventory issuer nor a native runtime +material. The complete expectation must be reviewed against this 50-package +lock; the two-package CPU fixtures are only unit-test inputs. + +Read-only source inspection on the installed Mac candidate found these hashes: + +- `mlx_lm/tuner/trainer.py`: + `0b6027fdb11803e951ceee52c83a81f82daa922ea8c6b7ca6888953461064e8e` +- `mlx_lm/tuner/datasets.py`: + `dcd675376d68d1813261c8389236b586b16b9a58a5ea80be8eb535bb9d0e9689` +- Candidate Python executable: + `b41507ef245eac19f76b6ed5a53fbc5eb49bf7b83b95b78414a50fccfe0bf0e7` +- Linked Homebrew Python framework: + `c5042528a85834df4ead67663e949dc938f7354f98c0c7b70c0f4a17830234e8` + +`otool -L` confirms that executable loads a separate Homebrew Python framework +and the system library. Its executable hash alone is not a native-library +closure. These observations involved no ML imports or GPU activity. See the [native desktop plan](../../../docs/plans/native-desktop-training-api-v1.md) for runtime material, provider lifecycle and artifact acceptance requirements. diff --git a/docs/plans/native-desktop-training-api-v1.md b/docs/plans/native-desktop-training-api-v1.md index 76ef7f64..0a212aeb 100644 --- a/docs/plans/native-desktop-training-api-v1.md +++ b/docs/plans/native-desktop-training-api-v1.md @@ -128,3 +128,72 @@ before the next live run. Both these observations are preparation evidence. Neither a reachable machine, installed package metadata, macOS base-import CI nor a successful container inspection qualifies local training. + +## Prepared SFT increment — 2026-10-20 + +Internal modules `tuner/runtime/native_mlx_sft.py` and +`tuner/runtime/native_mlx_runtime.py` implement a candidate preparation/execution +boundary without changing `RuntimeSpec`, `ProviderFamilyV1`, the legacy trainer, +or the public API. The worker consumes the existing shared tokenized examples, +retains host-provided row/split/source/tokenizer identities, and copies their +tokens and labels. It never loads text through MLX-LM's dataset wrappers. A +canonical projection digest identifies the copied data and worker controls; +it is not proof of source derivation or a replacement prepared publication. + +The custom batch iterator retains every row, including small and partial +batches, and validation order. The custom causal loss uses each shifted label, +substitutes a safe gather target for ignored labels, and divides by the number +of supervised tokens. Explicit finite controls cover iteration count, sequence +and batch-token bounds, shuffle seed, and report/evaluate/save intervals. This +first boundary permits one batch per update, no distributed execution, no +packing, no gradient checkpointing, and unpadded causal examples only. It rejects +failed preprocessing diagnostics and truncation instead of repairing rows. + +Candidate runtime preparation compares the caller's reviewed complete package +versions, dependency-lock digest, CPython version and interpreter bytes before +the worker imports MLX. It is read-only and returns `CANDIDATE_MATCH_ONLY`. +Package metadata equality is not wheel-byte or native-library attestation. +The checked-in requirements lock and its pins are unchanged. + +### Remaining joins, in dependency order + +1. **Native material and cold preparation.** Specify a versioned native runtime + material/profile with exact source closure, interpreter plus linked native + libraries, selected wheel bytes and the configured model/tokenizer revisions. + Revalidate in the child before ML/model imports. Preserve existing canonical + documents. A caller-supplied expectation or a matching report is not authority. +2. **Prepared-input host join.** Reuse the existing retained prepared-input source + and shared tokenizer materialization under authenticated model/tokenizer + pins. Bind the exact row IDs, splits, template arguments and projection + digest to the compiled workload. The new projection accepts already prepared + objects; it cannot independently prove their association with caller-provided + source identity. Source lineage/held-out derivative checks remain upstream. +3. **Model/optimizer child composition.** Prepare the pinned offline model, + explicitly configured LoRA targets/layers/rank/scale and optimizer/schedule. + Verify trainable parameters and the applicable model's causal behavior. + Never translate unsupported training knobs into silent MLX defaults. +4. **Bounded native child lifecycle.** The worker's finite iterations do not bound + a hung Metal call, model load, validation, save, or stdout. The host needs an + owned process group and identity, clean environment, bounded private I/O, + wall-clock limit, stdout/stderr limits, explicit termination/wait escalation, + and durable claim/spawn/identity retention. Test owner death and restart at + each boundary. Retain uncertainty after lost spawn acknowledgement; never + replay automatically. Existing `shared/environments/local_worker.py` and + `bounded_local_runtime.py` are Linux-specific fixture workflows, not a + qualified macOS training supervisor. Do not repurpose them without review. +5. **Native artifacts and API provider.** Bind authentic metrics/workload/lineage, + MLX adapter and tokenizer roles to the exact run, verify bounded streams, and + connect the existing `TrainingAPI`/`RunsAPI` ports to host-owned durable stores. + Upstream saves and console text are not verification or durable completion. + Keep known-run reads, cancellation request, confirmed termination, and + verified artifact publication distinct. No engine database is needed. +6. **Real Mac/Syntunia qualification.** Independently compare raw-text, + prompt/completion and noncontiguous mask fixtures through the real pinned + tokenizer and MLX loss/autograd; demonstrate changed adapter parameters, + held-out evaluation and verified artifacts on the M4. Then exercise bounded + stop, failure/restart and consumer readback. Syntunia should keep native + training unavailable until these joins pass; native GRPO remains separate. + +CPU contract tests prove projection/control behavior and scalar loss algebra, +not MLX graph compilation, Metal memory capacity, model compatibility, native +artifact validity, process lifecycle, or successful training. diff --git a/docs/review/offline-sft-worker-diagnostics.md b/docs/review/offline-sft-worker-diagnostics.md new file mode 100644 index 00000000..4c0701dd --- /dev/null +++ b/docs/review/offline-sft-worker-diagnostics.md @@ -0,0 +1,88 @@ +# Offline SFT worker diagnostic boundary — 2026-10-20 + +The Docker attempt on engine `9a36aaaac30877c8a21da6f6275047eefbc773b1` +and host `2e774416f8b70910824ba9c4fdf91f2f2c505218` failed with no artifacts. +Its pinned image was +`sha256:e737a0a091474c4892690690e712bc1f932f108856a3d5df60383e1cd9436e1e`. +Container `07ecde6d5b798abf51eb85595be165039cdc1043295cbd0a5b0d3d526064bfd7` +exited 23 at 12:28:41Z with `OOMKilled=false`. The retained child stderr was +only `OFFLINE_SFT_WORKER_REJECTED`; stdout was empty. These facts establish +worker invocation, not trainer entrypoint execution, a missing dependency, +OOM, or a cause. + +The retained 6,349-byte workload was read without running a container or +trainer and matched SHA-256 +`f33b68e492571e9ea73263519c8885c45900961e93465e91744144939688fcc8`. +Its raw-text, three-step, non-4-bit SFT configuration projected through the +existing `_append_sft_arguments` into 34 arguments accepted by the worker +validator. This checks the configuration-derived argument tail; the original +child argv was not retained. The earlier exact 68-member staged closure check +also passed. Worker and argument-builder source were unchanged between the +attempted commit and the implementation base `c14fc325`. + +The existing two-generation CPU test exercises runtime decoding, invocation +construction, closure validation, and guarded source import with a substituted +trainer. It passed before this change. Neither that test nor argument validation +reproduced the original rejection. No ML import, model load or training was +performed during this diagnosis. A read-only Docker inspection over WSL failed +with `Failed to initialize: protocol not available`; ordinary retained-file +reads worked. No Docker state was changed. + +## Change and scope + +The worker now emits finite phase/category labels on its existing stderr path, +plus an optional last admitted trainer milestone through the already-existing +`__packaged_runtime_phase__` hook. It retains failure exit code 2, exact closure +validation, argument validation and import-origin checks. Exception messages, +arbitrary exception attributes, paths, arguments and traceback frames do not +enter the diagnostic. Unknown milestones clear the previous marker. A normal +zero `SystemExit` still passes the final owned-module origin check; nonzero or +text exits fail closed. Unclassified failures retain the generic marker. + +This change adds diagnosis, not a demonstrated repair of the historical +failure. It does not classify Torch OOM or identify missing imports. Library +output is separate from the worker's own bounded diagnostic line. No profile, +model, image, runtime, dependency or hardware pin is changed. + +CPU regressions cover every worker phase, exception categories, all existing +finite trainer hooks, hostile exception formatting/attribute methods, unknown +hooks, silent success, postcheck after zero exit, guard refusal of an unlisted +owned import, and raw-text/message argument-builder agreement. Subprocess +fixtures execute the actual isolated bootstrap and staged closure, substitute +only the trainer boundary, and assert that Torch, Unsloth, Transformers and +TRL were never imported. + +Validation on the implementation branch: + +- Worker and SFT runtime suites: **182 passed**. +- SFT/GRPO/packaged/HF closure maintenance, Modal runtime/inference lock + contracts and packaged-child suites: **248 passed, 1 skipped**. The skip + requires Torch, intentionally absent from the supplied nontraining venv. +- All seven checked-in manifest/lock maintenance checks: **CURRENT**. + Recursive before/after comparison confirmed unchanged keys, inventories, + ordering and non-hash pins; only hashes and byte counts changed. +- Canonical skill mirror check, Ruff 0.16.10 on changed Python files and + `git diff --check`: passed. + +Tests used the supplied nontraining CPython 3.12 interpreter with +`-B -m pytest --noconftest -q -o addopts=''` and explicit test files, one +process at a time. These suites define their own fixtures. The repository +root conftest imports unrelated NumPy/Pandas data fixtures, absent from that +venv; the initial unfiltered command stopped at the NumPy import before +collection. No packages were installed into the shared test environment. +The system Python also lacked `packaging` for the inference-lock maintainer; +its successful check/write/check used the supplied test interpreter. + +## Fresh qualification prerequisite + +Root must independently review and integrate the change, verify every affected +fixed closure and Modal training/inference lock, and build/install the exact +new engine wheel. The fresh host must pin that pushed engine gitlink, bind its +new closure digests, and retain a fresh source admission and consumer material +before any effect. Use a genuinely new run/request/grant and private roots, +with the normal checked-in preparation and public consumer commands. Preserve +all old consumed claims, containers and cache evidence. HF remains paused. + +A fresh bounded qualification can establish its own result or retain the new +phase/category/milestone for a targeted follow-up. It cannot retroactively +localize the old failure, prove writing quality, or authorize replay. diff --git a/examples/training_qualification/README.md b/examples/training_qualification/README.md index 0c53fe45..8f3c8ee2 100644 --- a/examples/training_qualification/README.md +++ b/examples/training_qualification/README.md @@ -177,6 +177,7 @@ Run from a neutral directory with the installed engine interpreter: ```sh python -I /absolute/checkout/examples/training_qualification/prepare_docker.py plan /private/prepare.json +python -I /absolute/checkout/examples/training_qualification/prepare_docker.py model-preflight /private/prepare.json python -I /absolute/checkout/examples/training_qualification/prepare_docker.py prepare /private/prepare.json python -I /absolute/checkout/examples/training_qualification/prepare_docker.py prepare-model /private/prepare.json python -I /absolute/checkout/examples/training_qualification/launch.py start /private/prepared/consumer.json @@ -202,6 +203,22 @@ create-only local attempt prevents automatic repeat after an uncertain download. Vendor download behavior is not a hard job deadline or bill cap. No manual weight upload, source inventory widening or restored training authority is involved. +Run `model-preflight` with the exact interpreter intended for `prepare-model` +before issuing short-lived source material. It checks the existing Hub SDK +signature contract without constructing a client, downloading, or creating +directories/claims. A base consumer installation can lack this optional SDK; +select an independently verified model-helper environment rather than upgrading +the pinned trainer or silently installing packages. `prepare-model` repeats +this check before its one-use claim. Compatibility is not package provenance, +an inventory lock, network readiness, or model/training qualification. + +Create the selected source/artifact roots' necessary private parents before +preparation; the preparer validates them and creates only its own leaf roots. +An existing model-preparation claim is consumed even if no receipt was produced. +Do not replay it. Failures now retain the model preparer's existing closed +`model_preparation_stage` when available, without exception messages or paths; +historical generic failures cannot be retrospectively assigned a stage. + Docker staging creates only the authenticated empty project directory plus its necessary parents, retaining their descriptors through submit checks. Extra files, directories, symlinks, replacement and permission changes still refuse. This diff --git a/examples/training_qualification/prepare_docker.py b/examples/training_qualification/prepare_docker.py index cede0c0a..60d66004 100644 --- a/examples/training_qualification/prepare_docker.py +++ b/examples/training_qualification/prepare_docker.py @@ -122,7 +122,12 @@ def filesystem_probe(configuration): _ZERO_QUOTE_DIGEST, _resource_digest, ) -from tuner.execution.providers.modal.model_snapshot import prepare_model_snapshot +from tuner.execution.providers.modal.model_snapshot import ( + MODEL_SNAPSHOT_PREPARATION_STAGES, + ModelSnapshotPreparationError, + _bind_hub_api, + prepare_model_snapshot, +) from tuner.project.context import ProjectContext from tuner.project.git_verification import ( GitCliLocalSourceInspector, @@ -371,12 +376,29 @@ def spec(name): } -def prepare_model(config, *, model_preparer=prepare_model_snapshot): +def model_preflight(): + """Check the existing SDK call contract before any one-use model claim.""" + try: + from huggingface_hub import HfApi, snapshot_download + _bind_hub_api(HfApi, snapshot_download) + except Exception: + raise ModelSnapshotPreparationError("SDK_ADMISSION") from None + return { + "model_sdk_contract": "compatible", + "model_download": False, + "provider_qualified": False, + } + + +def prepare_model( + config, *, model_preparer=prepare_model_snapshot, sdk_preflight=model_preflight, +): """Explicit pinned public download; never writes the sealed source mount.""" from contextlib import ExitStack from tuner.execution.providers.docker_provider_v1.local_source import _Root from Trainers.sft.runtime_v1 import RuntimeRoots, _require_local_model_snapshot + sdk_preflight() output = Path(config["output_root"]) launch._private(output) consumer = launch.load_config(output / "consumer.json") @@ -479,7 +501,7 @@ def prepare_model(config, *, model_preparer=prepare_model_snapshot): def main(argv=None): parser = argparse.ArgumentParser(description=__doc__) - parser.add_argument("action", choices=("plan", "prepare", "prepare-model")) + parser.add_argument("action", choices=("plan", "prepare", "model-preflight", "prepare-model")) parser.add_argument("configuration", type=Path) args = parser.parse_args(argv) try: @@ -491,6 +513,8 @@ def main(argv=None): "model_download": False, "provider_qualified": False, } + elif args.action == "model-preflight": + result = model_preflight() elif args.action == "prepare-model": result = prepare_model(config) else: @@ -536,8 +560,15 @@ def main(argv=None): result = prepare( config, pushed_sources=verifier, authenticator=auth, clock=clock ) - except Exception: - print(json.dumps({"diagnostic_code": "docker_material_preparation_failed"})) + except Exception as error: + diagnostic = {"diagnostic_code": "docker_material_preparation_failed"} + if ( + type(error) is ModelSnapshotPreparationError + and type(error.stage) is str + and error.stage in MODEL_SNAPSHOT_PREPARATION_STAGES + ): + diagnostic["model_preparation_stage"] = error.stage + print(json.dumps(diagnostic)) return 2 print(json.dumps(result, allow_nan=False)) return 0 diff --git a/tests/examples/test_docker_material_preparation.py b/tests/examples/test_docker_material_preparation.py index fcb93b24..ff47087c 100644 --- a/tests/examples/test_docker_material_preparation.py +++ b/tests/examples/test_docker_material_preparation.py @@ -167,7 +167,7 @@ def model_preparer(**kwargs): (target / "synthetic-config.json").write_text('{"fixture":true}') return target - result = helper.prepare_model(c, model_preparer=model_preparer) + result = helper.prepare_model(c, model_preparer=model_preparer, sdk_preflight=lambda: None) assert calls[0]["token"] is False assert ( calls[0]["revision"] @@ -179,7 +179,7 @@ def model_preparer(**kwargs): Path(c["consumer"]["docker"]["artifact_host_root"]) ) with pytest.raises(FileExistsError): - helper.prepare_model(c, model_preparer=model_preparer) + helper.prepare_model(c, model_preparer=model_preparer, sdk_preflight=lambda: None) assert len(calls) == 1 @@ -203,10 +203,111 @@ def test_hostile_model_roots_refuse_before_model_calls(configured, kind): artifact.chmod(0o755) with pytest.raises(ValueError): helper.prepare_model( - c, model_preparer=lambda **kwargs: pytest.fail("no model dispatch") + c, model_preparer=lambda **kwargs: pytest.fail("no model dispatch"), + sdk_preflight=lambda: None, ) +def test_model_sdk_failure_precedes_directories_and_claim(configured): + c, verifier, auth, clock, _ = configured + helper.prepare(c, pushed_sources=verifier, authenticator=auth, clock=clock) + + def missing_sdk(): + raise helper.ModelSnapshotPreparationError("SDK_ADMISSION") + + with pytest.raises(helper.ModelSnapshotPreparationError): + helper.prepare_model( + c, sdk_preflight=missing_sdk, + model_preparer=lambda **kwargs: pytest.fail("no model dispatch"), + ) + assert not (Path(c["output_root"]) / "model-preparation-attempt.json").exists() + assert not Path(c["consumer"]["docker"]["source_host_root"]).exists() + assert not Path(c["consumer"]["docker"]["artifact_host_root"]).exists() + + +def test_model_preflight_missing_sdk_is_closed(monkeypatch): + import builtins + original = builtins.__import__ + + def guarded(name, *args, **kwargs): + if name == "huggingface_hub": + raise ModuleNotFoundError("private path and credential text") + return original(name, *args, **kwargs) + + monkeypatch.setattr(builtins, "__import__", guarded) + with pytest.raises(helper.ModelSnapshotPreparationError) as caught: + helper.model_preflight() + assert caught.value.stage == "SDK_ADMISSION" + assert str(caught.value) == "model preparation failed" + assert caught.value.__suppress_context__ is True + + +def test_cli_model_preflight_checks_signatures_without_client_or_download( + tmp_path, monkeypatch, capsys, +): + import sys + import types + + class HfApi: + def __init__(self, *, endpoint, token): + pytest.fail("client construction") + + def model_info(self, repo_id, *, revision, files_metadata, token): + pytest.fail("metadata request") + + def snapshot_download( + *, repo_id, revision, token, endpoint, local_dir, cache_dir, + allow_patterns, max_workers, + ): + pytest.fail("download request") + + monkeypatch.setitem( + sys.modules, "huggingface_hub", + types.SimpleNamespace(HfApi=HfApi, snapshot_download=snapshot_download), + ) + config = tmp_path / "config.json" + config.write_text("{}") + assert helper.main(["model-preflight", str(config)]) == 0 + assert json.loads(capsys.readouterr().out) == { + "model_sdk_contract": "compatible", + "model_download": False, + "provider_qualified": False, + } + assert set(tmp_path.iterdir()) == {config} + + +@pytest.mark.parametrize("stage", ["SDK_ADMISSION", "DOWNLOAD", "DESTINATION_VERIFICATION"]) +def test_cli_retains_only_existing_closed_model_stage(tmp_path, monkeypatch, capsys, stage): + config = tmp_path / "config.json" + config.write_text("{}") + + def fail(_): + raise helper.ModelSnapshotPreparationError(stage) + + monkeypatch.setattr(helper, "prepare_model", fail) + assert helper.main(["prepare-model", str(config)]) == 2 + assert json.loads(capsys.readouterr().out) == { + "diagnostic_code": "docker_material_preparation_failed", + "model_preparation_stage": stage, + } + + +def test_cli_unknown_exception_does_not_leak_text_or_stage(tmp_path, monkeypatch, capsys): + config = tmp_path / "config.json" + config.write_text("{}") + + def fail(_): + error = RuntimeError("private path and credential text") + error.stage = "private stage" + raise error + + monkeypatch.setattr(helper, "prepare_model", fail) + assert helper.main(["prepare-model", str(config)]) == 2 + assert json.loads(capsys.readouterr().out) == { + "diagnostic_code": "docker_material_preparation_failed", + } + + def test_actual_native_writer_cpu_filesystem_probe_and_host_read(tmp_path): import hashlib import tuner diff --git a/tests/runtime/test_native_mlx_runtime.py b/tests/runtime/test_native_mlx_runtime.py new file mode 100644 index 00000000..987c149d --- /dev/null +++ b/tests/runtime/test_native_mlx_runtime.py @@ -0,0 +1,71 @@ +from dataclasses import replace +import hashlib +from types import SimpleNamespace + +import pytest + +from tuner.runtime import native_mlx_runtime as runtime + + +@pytest.fixture +def observed(monkeypatch, tmp_path): + executable = tmp_path / "python" + executable.write_bytes(b"candidate interpreter") + monkeypatch.setattr(runtime.sys, "executable", str(executable)) + monkeypatch.setattr(runtime.platform, "system", lambda: "Darwin") + monkeypatch.setattr(runtime.platform, "machine", lambda: "arm64") + monkeypatch.setattr(runtime.platform, "python_version", lambda: "3.11.14") + inventory = [ + SimpleNamespace(metadata={"Name": "mlx"}, version="0.32.2"), + SimpleNamespace(metadata={"Name": "mlx_lm"}, version="0.32.2"), + ] + monkeypatch.setattr(runtime.metadata, "distributions", lambda: iter(inventory)) + expected = runtime.MLXRuntimeExpectation.from_dict({ + "python_version": "3.11.14", + "executable_sha256": hashlib.sha256(executable.read_bytes()).hexdigest(), + "dependency_lock_sha256": hashlib.sha256(b"lock bytes").hexdigest(), + "distributions": {"mlx": "0.32.2", "mlx-lm": "0.32.2"}, + }) + return expected, inventory, executable + + +def test_candidate_comparison_is_read_only_and_explicitly_not_qualification(observed): + expected, _, executable = observed + result = runtime.inspect_mlx_runtime_candidate(expected, dependency_lock=b"lock bytes") + assert result["status"] == "CANDIDATE_MATCH_ONLY" + assert result["distributions"] == dict(expected.distributions) + assert executable.read_bytes() == b"candidate interpreter" + + +@pytest.mark.parametrize("change", ["extra", "missing", "version", "duplicate", "executable", "python", "platform", "lock"]) +def test_runtime_drift_refuses_before_any_framework_import(observed, monkeypatch, change): + expected, inventory, executable = observed + lock = b"lock bytes" + if change == "extra": + inventory.append(SimpleNamespace(metadata={"Name": "extra"}, version="1.0")) + elif change == "missing": + inventory.pop() + elif change == "version": + inventory[0].version = "0.32.3" + elif change == "duplicate": + inventory.append(SimpleNamespace(metadata={"Name": "MLX.LM"}, version="0.32.2")) + elif change == "executable": + executable.write_bytes(b"replacement") + elif change == "python": + expected = replace(expected, python_version="3.12.0") + elif change == "platform": + monkeypatch.setattr(runtime.platform, "system", lambda: "Linux") + elif change == "lock": + lock = b"other lock" + with pytest.raises(ValueError): + runtime.inspect_mlx_runtime_candidate(expected, dependency_lock=lock) + + +def test_expectation_rejects_partial_pins_and_mutable_inventory(observed): + expected, _, _ = observed + with pytest.raises(ValueError): + replace(expected, distributions=(("mlx", "0.32.2"),)) + with pytest.raises(ValueError): + replace(expected, distributions=list(expected.distributions)) + with pytest.raises(ValueError): + replace(expected, executable_sha256="unknown") diff --git a/tests/runtime/test_native_mlx_sft.py b/tests/runtime/test_native_mlx_sft.py new file mode 100644 index 00000000..8656de6f --- /dev/null +++ b/tests/runtime/test_native_mlx_sft.py @@ -0,0 +1,308 @@ +from dataclasses import replace +import itertools +import math +from pathlib import Path +import subprocess +import sys +from types import ModuleType, SimpleNamespace + +import pytest + +from shared.sft_preprocessing import PreparedSFTExample, materialize_sft_example +from synaptic_tuner.api.v1.training_sources import PreparedTrainingInputIdentity +from tuner.runtime.native_mlx_sft import ( + MLXSFTConfig, iter_prepared_batches, make_mlx_sft_hooks, + prepare_mlx_sft, run_prepared_mlx_sft, +) + + +def config(**changes): + values = dict( + batch_size=2, max_seq_length=8, max_batch_tokens=16, iterations=3, + seed=17, steps_per_report=1, steps_per_eval=2, steps_per_save=3, + ) + values.update(changes) + return MLXSFTConfig.from_dict(values) + + +def example(tokens=(1, 2, 3, 4), labels=(-100, 2, -100, 4)): + return PreparedSFTExample( + list(tokens), [1] * len(tokens), list(labels), "messages", + "assistant_only", False, + ) + + +def prepared(*, train=None, validation=None, controls=None): + return prepare_mlx_sft( + source=PreparedTrainingInputIdentity( + "prepared://sha256/" + "a" * 64, "a" * 64, "b" * 64, 10, + "synaptic-conversation-sft-row/v1", + ), + tokenizer_revision="c" * 40, + train=train if train is not None else tuple( + (f"train-{i}", example()) for i in range(3) + ), + validation=validation if validation is not None else ( + ("heldout-0", example()), ("heldout-1", example((1, 3), (-100, 3))), + ), + config=controls or config(), + ) + + +def test_import_has_no_ml_or_provider_effects(): + root = Path(__file__).resolve().parents[2] + result = subprocess.run( + [sys.executable, "-c", ( + "import sys; import tuner.runtime.native_mlx_sft; " + "assert not any(k.split('.')[0] in " + "{'mlx', 'mlx_lm', 'torch', 'transformers', 'modal', 'numpy'} " + "for k in sys.modules)" + )], cwd=root, capture_output=True, timeout=15, + ) + assert result.returncode == 0, result.stderr.decode() + + +def test_copy_keeps_arbitrary_masks_splits_and_digest_stable(): + row = example() + data = prepared(train=(("train", row),)) + before = data.canonical_bytes() + row.input_ids[0] = 99 + row.labels[1] = -100 + assert data.train[0].input_ids == (1, 2, 3, 4) + assert data.train[0].labels == (-100, 2, -100, 4) + assert data.canonical_bytes() == before + assert len(data.digest) == 64 + assert data.digest != prepared(train=(("different-id", example()),)).digest + assert data.digest != replace(data, tokenizer_revision="d" * 40).digest + + +def test_shared_raw_text_materialization_is_not_retokenized_or_given_extra_eos(): + class Tokenizer: + eos_token_id = 7 + + def encode(self, text, add_special_tokens): + assert text == "text" and add_special_tokens is False + return [1, 2] + + row = materialize_sft_example( + tokenizer=Tokenizer(), + record={"schema_version": "syntunia-sft-row/v1", "format": "raw_text", "text": "text", + "split": "train"}, + max_seq_length=8, assistant_only_loss=False, + ) + data = prepared(train=(("raw", row),)) + assert data.train[0].input_ids == (1, 2, 7) + assert data.train[0].labels == (1, 2, 7) + + +def test_shared_prompt_completion_keeps_template_options_and_terminal_boundary(): + class Tokenizer: + eos_token_id = 999 + all_special_ids = [ord("!")] + + def apply_chat_template(self, messages, tokenize, add_generation_prompt, **kwargs): + assert tokenize is False and kwargs == {"enable_thinking": False} + text = "|".join(message["content"] for message in messages) + return text + ("|assistant:" if add_generation_prompt else "!") + + def encode(self, text, add_special_tokens): + assert add_special_tokens is False + return list(map(ord, text)) + + row = materialize_sft_example( + tokenizer=Tokenizer(), record={ + "schema_version": "syntunia-sft-row/v2", "format": "messages", + "split": "train", "messages": [ + {"role": "user", "content": "prompt"}, + {"role": "assistant", "content": "target"}, + ], + }, + max_seq_length=64, assistant_only_loss=True, prompt_render="prompt_completion", + chat_template_kwargs={"enable_thinking": False}, + ) + data = prepared(train=(("message", row),), controls=config( + max_seq_length=64, max_batch_tokens=128, + )) + assert data.train[0].input_ids == tuple(map(ord, "prompt|assistant:target!")) + assert data.train[0].labels == (-100,) * len("prompt|assistant:") + tuple(map(ord, "target!")) + assert 999 not in data.train[0].input_ids + + +@pytest.mark.parametrize("changes", [ + {"truncation_applied": True}, {"drop_reason": "no_supervised_tokens"}, + {"mask_prefix_mismatch": True}, {"mask_fallback_reason": "fallback"}, + {"input_ids": [1, True, 3, 4]}, {"labels": [-100, 99, -100, 4]}, + {"labels": [-100, True, -100, 4]}, {"attention_mask": [1, 0, 1, 1]}, + {"attention_mask": [1, 1]}, {"labels": [1, -100, -100, -100]}, + {"input_ids": [1] * 9, "attention_mask": [1] * 9, "labels": [1] * 9}, +]) +def test_invalid_prepared_rows_fail_without_repair(changes): + row = replace(example(), **changes) + with pytest.raises((ValueError, TypeError)): + prepared(train=(("bad", row),)) + + +def test_rejects_empty_splits_and_holdout_id_overlap(): + with pytest.raises(ValueError, match="nonempty"): + prepared(validation=()) + with pytest.raises(ValueError, match="more than once"): + prepared(train=(("heldout-0", example()),)) + + +@pytest.mark.parametrize("changes", [ + {"batch_size": True}, {"iterations": 0}, {"seed": -1}, + {"max_batch_tokens": 15}, {"gradient_accumulation_steps": 2}, + {"packing": True}, {"method": "grpo"}, +]) +def test_controls_fail_closed(changes): + with pytest.raises(ValueError): + config(**changes) + + +def test_batches_keep_tail_all_validation_and_exact_shiftable_labels(): + data = prepared() + batches = list(iter_prepared_batches(data, split="train")) + assert sorted(i for batch in batches for i in batch.row_ids) == [ + "train-0", "train-1", "train-2", + ] + assert [len(batch.row_ids) for batch in batches] == [2, 1] + assert batches == list(iter_prepared_batches(data, split="train")) + validation = list(iter_prepared_batches(data, split="validation")) + assert validation[0].row_ids == ("heldout-0", "heldout-1") + assert validation[0].input_ids == ((1, 2, 3, 4), (1, 3, 0, 0)) + assert validation[0].labels == ((-100, 2, -100, 4), (-100, 3, -100, -100)) + looped = list(itertools.islice(iter_prepared_batches(data, split="train", loop=True), 4)) + assert sorted(i for batch in looped[2:] for i in batch.row_ids) == [ + "train-0", "train-1", "train-2", + ] + with pytest.raises(ValueError): + list(iter_prepared_batches(data, split="validation", loop=True)) + + +class Matrix: + """Small CPU array double; CE below uses independent scalar probability math.""" + + def __init__(self, rows, dtype=None): + self.rows = [list(row) for row in rows] + + def __getitem__(self, key): + rows, columns = key + return Matrix([row[columns] for row in self.rows[rows]]) + + def __ne__(self, value): + return Matrix([[item != value for item in row] for row in self.rows]) + + def sum(self): + return sum(sum(row) for row in self.rows) + + def astype(self, dtype): + return self + + +def where(mask, left, right): + return Matrix([ + [(left.rows[i][j] if isinstance(left, Matrix) else left) if item + else (right.rows[i][j] if isinstance(right, Matrix) else right) + for j, item in enumerate(row)] + for i, row in enumerate(mask.rows) + ]) + + +MX = SimpleNamespace(array=Matrix, where=where, int32="int32", float32="float32") + + +def test_loss_uses_shifted_per_token_targets_and_supervised_token_denominator(): + data = prepared() + observed = {} + + def model(inputs): + observed["inputs"] = inputs.rows + # Precomputed logits are log probabilities; every position sums to one. + return [[[math.log(p) for p in (.1, .1, .2, .3, .3)]] * 3] * 2 + + def cross_entropy(logits, targets): + observed["targets"] = targets.rows + # Reject negative gather indices, just as the real loss must never see -100. + assert all(t >= 0 for row in targets.rows for t in row) + return Matrix([ + [math.log(sum(math.exp(x) for x in vector)) - vector[target] + for vector, target in zip(row, target_row)] + for row, target_row in zip(logits, targets.rows) + ]) + + iterate, loss = make_mlx_sft_hooks( + data, mx=MX, nn=SimpleNamespace(losses=SimpleNamespace(cross_entropy=cross_entropy)), + ) + batch = next(iterate(data.validation, 2, 8)) + value, count = loss(model, *batch) + assert observed["inputs"] == [[1, 2, 3], [1, 3, 0]] + assert observed["targets"] == [[2, 0, 4], [3, 0, 0]] + assert count == 3 + assert value == pytest.approx((-math.log(.2) - 2 * math.log(.3)) / 3) + + +def test_hooks_refuse_changed_or_distributed_upstream_calls(): + data = prepared() + iterate, _ = make_mlx_sft_hooks(data, mx=MX, nn=None) + for kwargs in ( + {"dataset": list(data.train)}, + {"batch_size": 1}, {"max_seq_length": 7}, {"seed": 19}, + {"comm_group": SimpleNamespace(size=lambda: 2, rank=lambda: 0)}, + ): + args = dict(dataset=data.train, batch_size=2, max_seq_length=8) + args.update(kwargs) + with pytest.raises(ValueError): + list(iterate(**args)) + + +def test_worker_joins_pinned_upstream_hooks_without_text_loader(monkeypatch, tmp_path): + data = prepared() + calls = {} + mx = ModuleType("mlx.core") + mx.__dict__.update(vars(MX)) + mx.random = SimpleNamespace(seed=lambda seed: calls.update(seed=seed)) + nn = ModuleType("mlx.nn") + trainer = ModuleType("mlx_lm.tuner.trainer") + trainer.TrainingArgs = lambda **kwargs: SimpleNamespace(**kwargs) + trainer.train = lambda **kwargs: calls.update(kwargs) + mlx = ModuleType("mlx") + mlx.core, mlx.nn = mx, nn + for name, module in ( + ("mlx", mlx), ("mlx.core", mx), ("mlx.nn", nn), + ("mlx_lm", ModuleType("mlx_lm")), + ("mlx_lm.tuner", ModuleType("mlx_lm.tuner")), + ("mlx_lm.tuner.trainer", trainer), + ): + monkeypatch.setitem(sys.modules, name, module) + model, optimizer, callback = object(), object(), object() + runtime = object() + def inspect(expected, *, dependency_lock): + assert expected is runtime and dependency_lock == b"reviewed lock" + calls["inspected"] = True + monkeypatch.setattr("tuner.runtime.native_mlx_sft.inspect_mlx_runtime_candidate", inspect) + run_prepared_mlx_sft( + data, model=model, optimizer=optimizer, + adapter_file=str(tmp_path / "adapter.safetensors"), training_callback=callback, + runtime=runtime, dependency_lock=b"reviewed lock", + ) + assert calls["model"] is model and calls["optimizer"] is optimizer + assert calls["train_dataset"] is data.train + assert calls["val_dataset"] is data.validation + assert calls["training_callback"] is callback + assert calls["seed"] == 17 + assert calls["args"].iters == 3 and calls["args"].val_batches == -1 + assert calls["args"].grad_accumulation_steps == 1 + assert calls["inspected"] + assert callable(calls["loss"]) and callable(calls["iterate_batches"]) + + +def test_runtime_rejection_happens_before_worker_imports(monkeypatch, tmp_path): + def reject(*args, **kwargs): + raise ValueError("candidate mismatch") + monkeypatch.setattr("tuner.runtime.native_mlx_sft.inspect_mlx_runtime_candidate", reject) + with pytest.raises(ValueError, match="candidate mismatch"): + run_prepared_mlx_sft( + prepared(), model=None, optimizer=None, + adapter_file=str(tmp_path / "adapter.safetensors"), + runtime=None, dependency_lock=b"bad", + ) diff --git a/tests/runtime/test_offline_sft_worker.py b/tests/runtime/test_offline_sft_worker.py index 5d1fe641..24c56a7a 100644 --- a/tests/runtime/test_offline_sft_worker.py +++ b/tests/runtime/test_offline_sft_worker.py @@ -213,7 +213,152 @@ def test_isolated_bootstrap_rejects_optional_feature_without_traceback( ) assert completed.returncode == 2 - assert completed.stderr.strip() == "OFFLINE_SFT_WORKER_REJECTED" + assert completed.stdout == "" + assert completed.stderr == ( + "OFFLINE_SFT_WORKER_REJECTED phase=ARGUMENTS category=REJECTION\n" + ) + + +def _run_bootstrap_fixture(tmp_path, setup="", trainer="return {}"): + """Real isolated bootstrap and closure; replace only the ML entrypoint.""" + engine, manifest, document = _stage(tmp_path) + code = """ +import os, runpy, sys +from pathlib import Path +from types import ModuleType +worker = runpy.run_path(sys.argv[1], run_name="fixture_bootstrap") +namespace = worker["main"].__globals__ +sys.argv = [sys.argv[1], "--", "--max-steps", "1", "--no-load-in-4bit"] +""" + setup + """ +def fixture_trainer(path, *, run_name, init_globals): + assert Path(path).name == "train_sft.py" + assert run_name == "__main__" + mark = init_globals["__packaged_runtime_phase__"] +""" + "\n".join(" " + line for line in trainer.splitlines()) + """ +runpy.run_path = fixture_trainer +result = worker["main"]() +assert not any(name in sys.modules for name in ("torch", "unsloth", "transformers", "trl")) +raise SystemExit(result) +""" + return subprocess.run( + [sys.executable, "-I", "-c", code, str(engine / _WORKER)], + env={ + **os.environ, + "SYNAPTIC_ENGINE_ROOT": str(engine), + "SYNAPTIC_WORKER_CLOSURE_MANIFEST": str(manifest), + "SYNAPTIC_WORKER_CLOSURE_DIGEST": document["closure_digest"], + "PYTHONNOUSERSITE": "1", + "PYTHONSAFEPATH": "1", + }, + stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=30, + ) + + +@pytest.mark.parametrize(("setup", "trainer", "phase", "category"), [ + ('sys.argv[1] = "private-delimiter"', "return {}", "BOOTSTRAP", "REJECTION"), + ('os.environ["SYNAPTIC_WORKER_CLOSURE_DIGEST"] = "0" * 64', + "return {}", "CLOSURE", "REJECTION"), + ('namespace["__file__"] = str(Path(os.environ["SYNAPTIC_ENGINE_ROOT"]) / "Trainers/sft/runtime_v1.py")', + "return {}", "BOOTSTRAP_ORIGIN", "REJECTION"), + ('sys.argv.append("--private-unsupported-flag")', "return {}", "ARGUMENTS", "REJECTION"), + ('sys.modules["shared.not_admitted"] = ModuleType("shared.not_admitted")', + "return {}", "IMPORT_GUARD", "REJECTION"), + ("", 'import shared.not_admitted', "TRAINER", "IMPORT"), + ("", 'sys.modules["shared.not_admitted"] = ModuleType("shared.not_admitted")\nreturn {}', + "POSTCHECK", "REJECTION"), + ("", 'raise OSError("private-path-and-token")', "TRAINER", "OS"), + ("", 'raise ValueError("private-input")', "TRAINER", "VALUE"), + ("", 'raise RuntimeError("private-library-error")', "TRAINER", "OTHER"), + ("", 'raise SystemExit("private-exit-text")', "TRAINER", "SYSTEM_EXIT"), + ("", 'raise KeyboardInterrupt("private-interrupt")', "TRAINER", "OTHER"), +]) +def test_isolated_worker_failures_are_closed(tmp_path, setup, trainer, phase, category): + completed = _run_bootstrap_fixture(tmp_path, setup, trainer) + assert completed.returncode == 2 + assert completed.stdout == "" + assert completed.stderr == ( + f"OFFLINE_SFT_WORKER_REJECTED phase={phase} category={category}\n" + ) + + +@pytest.mark.parametrize("milestone", sorted(offline_sft_worker._TRAINER_MILESTONES)) +def test_trainer_import_failure_retains_only_finite_milestone(tmp_path, milestone): + completed = _run_bootstrap_fixture( + tmp_path, trainer=f'mark({milestone!r})\nraise ImportError("private-dependency")', + ) + assert completed.returncode == 2 + assert completed.stdout == "" + assert completed.stderr == ( + "OFFLINE_SFT_WORKER_REJECTED phase=TRAINER category=IMPORT" + f" milestone={milestone}\n" + ) + assert len(completed.stderr.encode()) < 160 + + +def test_invalid_milestone_and_hostile_exception_are_not_rendered(tmp_path): + completed = _run_bootstrap_fixture(tmp_path, trainer=''' +class HostileError(ImportError): + def __str__(self): + raise AssertionError("exception text inspected") + def __getattribute__(self, name): + raise AssertionError("exception attribute inspected") +mark("TORCH_IMPORT") +mark("private-unknown-milestone") +raise HostileError("private-payload") +''') + assert completed.returncode == 2 + assert completed.stdout == "" + assert completed.stderr == "OFFLINE_SFT_WORKER_REJECTED phase=TRAINER category=IMPORT\n" + + +@pytest.mark.parametrize("trainer", ["return {}", "raise SystemExit(0)", "raise SystemExit(None)"]) +def test_successful_fixture_preserves_postcheck_and_silent_success(tmp_path, trainer): + completed = _run_bootstrap_fixture(tmp_path, trainer=trainer) + assert completed.returncode == 0 + assert completed.stdout == completed.stderr == "" + + +def test_zero_exit_cannot_skip_owned_origin_postcheck(tmp_path): + completed = _run_bootstrap_fixture(tmp_path, trainer=''' +mark("SAVE") +sys.modules["shared.not_admitted"] = ModuleType("shared.not_admitted") +raise SystemExit(0) +''') + assert completed.returncode == 2 + assert completed.stderr == "OFFLINE_SFT_WORKER_REJECTED phase=POSTCHECK category=REJECTION\n" + + +def test_unclassified_main_failure_uses_generic_fallback(monkeypatch, capsys): + def reject(): + raise ValueError("private-unclassified") + monkeypatch.setattr(offline_sft_worker, "run_offline_sft_worker", reject) + assert offline_sft_worker.main() == 2 + assert capsys.readouterr().err == "OFFLINE_SFT_WORKER_REJECTED\n" + + +def test_worker_milestones_match_existing_packaged_child(): + from tuner.runtime.packaged_sft_child import _EXEC_RUNTIME_MILESTONES + assert offline_sft_worker._TRAINER_MILESTONES == frozenset(_EXEC_RUNTIME_MILESTONES) + + +@pytest.mark.parametrize("dataset_format", ["raw_text", "messages"]) +def test_runtime_argument_builder_passes_offline_allowlist(dataset_format): + from Trainers.sft.runtime_v1 import _append_sft_arguments + + # Same controls as the retained three-step qualification, with no model, + # dataset contents, private paths, or training execution required. + sft = { + "dataset_format": dataset_format, "batch_size": 1, + "gradient_accumulation_steps": 1, "learning_rate": "0.0001", + "max_steps": 3, "max_seq_length": 2048, "seed": 7, "save_steps": 3, + "save_total_limit": 1, "lora_rank": 8, "lora_alpha": 16, + "lora_dropout": "0.0", "lora_target_modules": ["q_proj", "v_proj"], + "use_dora": False, "use_rslora": False, "init_lora_weights": True, + "split_dataset": False, + } + arguments = [] + _append_sft_arguments(arguments, sft, {"load_in_4bit": False}) + offline_sft_worker._validate_trainer_arguments(arguments) _WORKER = "tuner/runtime/offline_sft_worker.py" diff --git a/tests/trainers/grpo/test_runtime_v1.py b/tests/trainers/grpo/test_runtime_v1.py index 412a85c7..6b5c78bd 100644 --- a/tests/trainers/grpo/test_runtime_v1.py +++ b/tests/trainers/grpo/test_runtime_v1.py @@ -102,6 +102,38 @@ def test_real_flow_offline_rewards_metrics_and_mandatory_roles(tmp_path, monkeyp assert 'parameter_updated' not in result['training_lineage'].read_text() +@pytest.mark.parametrize(('fp16', 'bf16', 'supported', 'expected'), [ + (True, False, True, 'float16'), + (False, True, True, 'bfloat16'), + (False, True, False, 'float16'), +]) +def test_model_load_precision_matches_effective_trainer_precision( + tmp_path, fp16, bf16, supported, expected): + values = inputs(tmp_path) + values['config']['training'].update(fp16=fp16, bf16=bf16) + values['workload']['config_sha256'] = runtime._sha(runtime._canonical(values['config'])) + events = [] + deps = dependencies(events)() + deps['bf16_supported'] = supported + def load(*args, **kwargs): + events.append(('model', args, kwargs)) + return SimpleNamespace(config=SimpleNamespace(dtype=kwargs['dtype'])), Tokenizer(), False + deps['model_loader'].load_model_and_tokenizer = load + trainer_base = deps['trainer_class'] + class PrecisionCheckingTrainer(trainer_base): + def __init__(self, **kwargs): + dtype = kwargs['model'].config.dtype + precision = kwargs['args'] + if (dtype == 'float16' and precision.bf16) or (dtype != 'float16' and precision.fp16): + raise TypeError('incompatible model and trainer precision') + super().__init__(**kwargs) + deps['trainer_class'] = PrecisionCheckingTrainer + runtime.run_grpo(**values, _dependency_factory=lambda: deps) + assert events[0][2]['dtype'] == expected + assert (events[1][1]['args'].fp16, events[1][1]['args'].bf16) == ( + expected == 'float16', expected == 'bfloat16') + + @pytest.mark.parametrize('change', ['dataset', 'reward', 'config', 'model', 'method', 'nonfinite', 'bool', 'custom', 'pivot', 'empty']) def test_rejects_before_dependency_effects(tmp_path, change): values = inputs(tmp_path) diff --git a/tests/trainers/sft/test_runtime_v1.py b/tests/trainers/sft/test_runtime_v1.py index 0d7cf294..9683ed50 100644 --- a/tests/trainers/sft/test_runtime_v1.py +++ b/tests/trainers/sft/test_runtime_v1.py @@ -452,8 +452,9 @@ def test_real_decoder_and_isolated_worker_preserve_exact_closure(tmp_path): from pathlib import Path assert sys.dont_write_bytecode is False bootstrap = runpy.run_path(sys.argv[1], run_name="fixture_bootstrap") -def fixture_trainer(path, *, run_name): +def fixture_trainer(path, *, run_name, init_globals): assert Path(path).name == "train_sft.py" + assert callable(init_globals["__packaged_runtime_phase__"]) import shared.env_bootstrap return {} runpy.run_path = fixture_trainer diff --git a/tuner/execution/providers/modal/inference-runtime.lock.json b/tuner/execution/providers/modal/inference-runtime.lock.json index d0792ed7..d7d654c5 100644 --- a/tuner/execution/providers/modal/inference-runtime.lock.json +++ b/tuner/execution/providers/modal/inference-runtime.lock.json @@ -1 +1 @@ -{"base_registry_reference":"docker.io/vllm/vllm-openai@sha256:116aa00ee0b68855616a56e1d7e1ae937e591a8bd6969ee45cbcedb246ddf355","dependency_lock_path":"tuner/execution/providers/modal/inference-dependencies.lock","distributions":{"accelerate":"1.13.0","aiofile":"3.9.0","aiofiles":"25.1.0","aiohappyeyeballs":"2.6.1","aiohttp":"3.13.3","aiosignal":"1.4.0","annotated-doc":"0.0.4","annotated-types":"0.7.0","anthropic":"0.84.0","anyio":"4.12.1","apache-tvm-ffi":"0.1.9","astor":"0.8.1","attrs":"25.4.0","awscrt":"0.31.3","bitsandbytes":"0.49.2","blake3":"1.0.8","boto3":"1.42.65","botocore":"1.42.65","cachetools":"7.0.5","caio":"0.9.25","cbor2":"5.8.0","certifi":"2026.2.25","cffi":"2.0.0","charset-normalizer":"3.4.5","click":"8.3.1","cloudpickle":"3.1.2","compressed-tensors":"0.13.0","cryptography":"46.0.5","cuda-bindings":"12.9.4","cuda-pathfinder":"1.4.2","cuda-python":"12.9.4","cufile-python":"0.2.0","cupy-cuda12x":"14.0.1","deep-ep":"1.2.1+73b6ea4","deep-gemm":"2.3.0+477618c","depyf":"0.20.0","dill":"0.4.1","diskcache":"5.6.3","distro":"1.9.0","dnspython":"2.8.0","docstring-parser":"0.17.0","einops":"0.8.2","email-validator":"2.3.0","fastapi":"0.135.1","fastapi-cli":"0.0.24","fastapi-cloud-cli":"0.14.1","fastar":"0.8.0","filelock":"3.25.1","flashinfer-cubin":"0.6.4","flashinfer-jit-cache":"0.6.4+cu129","flashinfer-python":"0.6.4","frozenlist":"1.8.0","fsspec":"2026.2.0","gguf":"0.18.0","google-api-core":"2.30.0","google-auth":"2.49.0","google-cloud-core":"2.5.0","google-cloud-storage":"3.9.0","google-crc32c":"1.8.0","google-resumable-media":"2.8.0","googleapis-common-protos":"1.73.0","grpcio":"1.78.0","grpcio-reflection":"1.78.0","grpclib":"0.4.9","h11":"0.16.0","h2":"4.4.1","hf-transfer":"0.1.9","hf-xet":"1.3.2","hpack":"4.2.0","httpcore":"1.0.9","httptools":"0.7.1","httpx":"0.28.1","httpx-sse":"0.4.3","huggingface-hub":"0.36.2","humanize":"4.15.0","hyperframe":"6.1.0","idna":"3.11","ijson":"3.5.0","importlib-metadata":"8.7.1","interegular":"0.3.3","jinja2":"3.1.6","jiter":"0.13.0","jmespath":"1.1.0","jsonschema":"4.26.0","jsonschema-specifications":"2025.9.1","kaldi-native-fbank":"1.22.3","lark":"1.2.2","llguidance":"1.3.0","llvmlite":"0.44.0","lm-format-enforcer":"0.11.3","lmcache":"0.4.0","loguru":"0.7.3","markdown-it-py":"4.0.0","markupsafe":"3.0.3","mcp":"1.26.0","mdurl":"0.1.2","mistral-common":"1.9.1","modal":"1.5.4","model-hosting-container-standards":"0.1.13","modelscope":"1.34.0","mooncake-transfer-engine":"0.3.9","mpmath":"1.3.0","msgpack":"1.1.2","msgspec":"0.20.0","multidict":"6.7.1","networkx":"3.6.1","ninja":"1.13.0","nixl":"0.9.0","nixl-cu12":"0.10.1","numba":"0.61.2","numpy":"2.2.6","nvidia-cublas-cu12":"12.9.1.4","nvidia-cuda-cupti-cu12":"12.9.79","nvidia-cuda-nvrtc-cu12":"12.9.86","nvidia-cuda-runtime-cu12":"12.9.79","nvidia-cudnn-cu12":"9.10.2.21","nvidia-cudnn-frontend":"1.18.0","nvidia-cufft-cu12":"11.4.1.4","nvidia-cufile-cu12":"1.14.1.1","nvidia-curand-cu12":"10.3.10.19","nvidia-cusolver-cu12":"11.7.5.82","nvidia-cusparse-cu12":"12.5.10.65","nvidia-cusparselt-cu12":"0.7.1","nvidia-cutlass-dsl":"4.4.1","nvidia-cutlass-dsl-libs-base":"4.4.1","nvidia-ml-py":"13.590.48","nvidia-nccl-cu12":"2.27.5","nvidia-nvjitlink-cu12":"12.9.86","nvidia-nvshmem-cu12":"3.4.5","nvidia-nvtx-cu12":"12.9.79","nvtx":"0.2.14","openai":"2.24.0","openai-harmony":"0.0.8","opencv-python-headless":"4.13.0.92","opentelemetry-api":"1.40.0","opentelemetry-exporter-otlp":"1.40.0","opentelemetry-exporter-otlp-proto-common":"1.40.0","opentelemetry-exporter-otlp-proto-grpc":"1.40.0","opentelemetry-exporter-otlp-proto-http":"1.40.0","opentelemetry-proto":"1.40.0","opentelemetry-sdk":"1.40.0","opentelemetry-semantic-conventions":"0.61b0","opentelemetry-semantic-conventions-ai":"0.4.15","outlines-core":"0.2.11","packaging":"26.0","partial-json-parser":"0.2.1.1.post7","pillow":"12.1.1","pip":"26.0.1","prometheus-client":"0.24.1","prometheus-fastapi-instrumentator":"7.1.0","propcache":"0.4.1","proto-plus":"1.27.1","protobuf":"6.33.5","psutil":"7.2.2","py-cpuinfo":"9.0.0","pyasn1":"0.6.2","pyasn1-modules":"0.4.2","pybase64":"1.4.3","pycountry":"26.2.16","pycparser":"3.0","pydantic":"2.12.5","pydantic-core":"2.41.5","pydantic-extra-types":"2.11.0","pydantic-settings":"2.13.1","pygments":"2.19.2","pyjwt":"2.11.0","python-dateutil":"2.9.0.post0","python-dotenv":"1.2.2","python-json-logger":"4.0.0","python-multipart":"0.0.22","pyyaml":"6.0.3","pyzmq":"27.1.0","quack-kernels":"0.3.2","ray":"2.54.0","redis":"7.3.0","referencing":"0.37.0","regex":"2026.2.28","requests":"2.32.5","rich":"14.3.3","rich-toolkit":"0.19.7","rignore":"0.7.6","rpds-py":"0.30.0","rsa":"4.9.1","runai-model-streamer":"0.15.7","runai-model-streamer-gcs":"0.15.7","runai-model-streamer-s3":"0.15.7","s3transfer":"0.16.0","safetensors":"0.7.0","sentencepiece":"0.2.1","sentry-sdk":"2.54.0","setproctitle":"1.3.7","setuptools":"80.10.2","setuptools-scm":"9.2.2","shellingham":"1.5.4","six":"1.17.0","sniffio":"1.3.1","sortedcontainers":"2.4.0","sse-starlette":"3.3.2","starlette":"0.52.1","supervisor":"4.3.0","sympy":"1.14.0","synaptic-tuner":"1.1.0","synchronicity":"0.12.5","tabulate":"0.10.0","tiktoken":"0.12.0","timm":"1.0.25","tokenizers":"0.22.2","toml":"0.10.2","torch":"2.10.0+cu129","torch-c-dlpack-ext":"0.1.5","torchaudio":"2.10.0+cu129","torchvision":"0.25.0+cu129","tqdm":"4.67.3","transformers":"4.57.6","triton":"3.6.0","typer":"0.24.1","types-certifi":"2021.10.8.3","types-toml":"0.10.8.20260518","typing-extensions":"4.15.0","typing-inspection":"0.4.2","urllib3":"2.6.3","uv":"0.10.9","uvicorn":"0.41.0","uvloop":"0.22.1","vllm":"0.17.1","watchfiles":"1.1.1","websockets":"16.0","xgrammar":"0.1.29","yarl":"1.23.0","zipp":"3.23.0"},"python":{"executable":"/opt/synaptic-inference/bin/python","executable_sha256":"185be5c01624944b5c422a8d1e4995a7df010036a99c9f9d84a80a09ae1bd72d","implementation":"cpython","version":"3.12.13"},"schema_version":"synaptic-modal-inference-runtime-lock/v1","sdk_version":"1.5.4","source_inventory":[{"path":"Evaluator/__init__.py","sha256":"d1b5aea793a95c47618d4b2469a8bae0b14fd11f81501c1d71546469a39a5cbd","size_bytes":3061},{"path":"Evaluator/base_client.py","sha256":"ac7e77909bdf161a4fd9b1ad2e1e2cfe085ec31f36baa6b19f8976f7b2358cbd","size_bytes":19362},{"path":"Evaluator/config.py","sha256":"94fbe8e198d065141ff9ad002b0a6e26e56d865fc1e3a3fb23479a8e69d09303","size_bytes":21956},{"path":"Evaluator/enums.py","sha256":"2ced9426d860e9ec410e376d9cf853d75e8e111e78dd0bee31e25bee295e4854","size_bytes":3371},{"path":"Evaluator/openai_compat_client.py","sha256":"6ac6211185d2c57d3b31022d31478e3253046fd6cb4de1903d8d0b209bad3a79","size_bytes":5174},{"path":"Evaluator/protocols.py","sha256":"239fe049b5fe4c9e9861aede487feac1f06a7642891378a1ccfba444072ad706","size_bytes":4560},{"path":"Evaluator/verified_vllm_chat.py","sha256":"f130e47d661aa77d68c104084fafe9e5df3af88684ef5a3139f4d53ff2e32465","size_bytes":5916},{"path":"Evaluator/vllm_client.py","sha256":"4e88d2055dc1b32cc22a1393cdf038ccd76d91d915a8be0774b0a8ec87bd4d1e","size_bytes":1995},{"path":"synaptic_tuner/__init__.py","sha256":"2462ff4ec439b77eb1ecfa901c1d8294628c853e9bbdf872de73dce9bfc0ae4d","size_bytes":134},{"path":"synaptic_tuner/_version.py","sha256":"49fa86b62849d537e7e131445f272650ed77760db5f218f2113155ab60caf644","size_bytes":96},{"path":"synaptic_tuner/api/__init__.py","sha256":"25c68ed273bf1e6b158021b98f8607276d980540a98b56a07801dccf9d6596a8","size_bytes":67},{"path":"synaptic_tuner/api/v1/__init__.py","sha256":"4bc30420b18b3932183dcd0485892fa22f4f8e474ca3bb9b5e08adf3ae0fde3d","size_bytes":16893},{"path":"synaptic_tuner/api/v1/_contract.py","sha256":"dcd6fc1ebdc75fbfd74ad4c6c96837b73209081258353557fedaaca76164673a","size_bytes":6702},{"path":"synaptic_tuner/api/v1/_timestamps.py","sha256":"2b2ad7183f62bf1d42c4b9144d0315fe90c0e601649fb37f7778d017c632935d","size_bytes":698},{"path":"synaptic_tuner/api/v1/planning.py","sha256":"e715b105a513fa56cfa510416e725471f8f3100cb34257be74bef3e40ca78460","size_bytes":9569},{"path":"synaptic_tuner/api/v1/providers.py","sha256":"eca5efd5a6dee453543ae338ffddf9451d34ce1743e4ab4bde024b47be571ec0","size_bytes":4940},{"path":"synaptic_tuner/api/v1/results.py","sha256":"65e530325c92c7afa18e3ca5f2ad9a11baf7d590221f08c33badfbf2d5f2c7b2","size_bytes":2236},{"path":"synaptic_tuner/api/v1/runs_facade.py","sha256":"bf6155726c99cfc5574385009e20f4f65b69a9ad4b96e33cf7065263d1d4c455","size_bytes":23572},{"path":"synaptic_tuner/api/v1/training_facade.py","sha256":"bf094d0e152f5861bf8a8ff51962e0700123d9b43ed91d914b9e78bbea6facd9","size_bytes":11118},{"path":"synaptic_tuner/api/v1/training_input.py","sha256":"209326325f8796ab7d038878e92def2c5521d7d98b7b9cc4dd1766d2e4f68c9d","size_bytes":38650},{"path":"tuner/__init__.py","sha256":"920cf8a0ca993a712fbacf734621e1fdf14fdd2a7203701db120dc41e4815e7b","size_bytes":2666},{"path":"tuner/cloud/__init__.py","sha256":"a9abb71aa9222c511524d3cc49b1cfe00f8938a6cb7c61792cf6135a6b82208b","size_bytes":3692},{"path":"tuner/cloud/runtime_layout.py","sha256":"2a4cd1f5fc63d95bb3f0b5bc4f44e616bb0308d9509aceb198943953746ff705","size_bytes":9435},{"path":"tuner/execution/__init__.py","sha256":"8193dfcd30c68a7d8ad02af0fb24f1a8120fce2a709decd132ab1acb33113d65","size_bytes":535},{"path":"tuner/execution/_effect_executor.py","sha256":"7aa3c523d971486417fe735c62a5cb3fe7e0698caa514d0da047293ad6b3e9cd","size_bytes":500},{"path":"tuner/execution/broker.py","sha256":"c504bd734b41ef9785fe5fa591966bb0b0bce3b43dcce0695b4ee0c142ca3d1e","size_bytes":6305},{"path":"tuner/execution/contracts.py","sha256":"82d90f742e4e33d8071e557b5a872e0158684d94db5debdfbb85411954e4e638","size_bytes":32907},{"path":"tuner/execution/coordinator_v1/__init__.py","sha256":"453718a1827cfb23e7ca0c908d3d256f62ca3e3fff8f75ed8a785f8336b17bbe","size_bytes":66},{"path":"tuner/execution/coordinator_v1/coordinator.py","sha256":"9bc8ecb60d472ad0b3c0d3192d8c73d88eb28dc43af55677fbdd4b3673b71ef9","size_bytes":47018},{"path":"tuner/execution/coordinator_v1/cursors.py","sha256":"5f7c899064dd26959aeed6b328b236b01f7ff549e2d29348bd45dc0480ede26b","size_bytes":9088},{"path":"tuner/execution/coordinator_v1/foundation.py","sha256":"4433c390075544df5516a5e213af4b4b697485ff7627f5a6dcace21a98029f94","size_bytes":12972},{"path":"tuner/execution/coordinator_v1/model.py","sha256":"1d2150d588f6b84f1b58913df398365a9491d6486a8a871bb2a22fa98cafc722","size_bytes":75837},{"path":"tuner/execution/coordinator_v1/ports.py","sha256":"7a028c32c3a3eab905bbc202248b4bf39411cd22e73fddacf5099b7d15dc3832","size_bytes":9343},{"path":"tuner/execution/coordinator_v1/state_machine.py","sha256":"e5e63db3a85fb9fc5b1d7bb5444691f88f308cfb0428842c0801b06d6f2fbb3a","size_bytes":50176},{"path":"tuner/execution/coordinator_v1/stores.py","sha256":"3a875f9e9d7d7d9899d5deba2f4d68cfada12bea499d5897592c9383ba2b16dc","size_bytes":39045},{"path":"tuner/execution/evidence.py","sha256":"a04cc6843a794e62a702608d28c8e82d0c57af8227b3cb9743cf56b9605442ca","size_bytes":4634},{"path":"tuner/execution/foundation_v2/__init__.py","sha256":"1651bf897879603a83e7824f60d369d52ac5d0308c96f988c24465f33112cd77","size_bytes":77},{"path":"tuner/execution/foundation_v2/authority.py","sha256":"a0eb5b3537a59c7b4d33e9b8617abd072376545d808f1989f562704dfa1ad024","size_bytes":9958},{"path":"tuner/execution/foundation_v2/canonical.py","sha256":"80620a6088d137bfacb210e000c406632bf569de66350628fb0e0aee612babfb","size_bytes":4047},{"path":"tuner/execution/foundation_v2/commands.py","sha256":"0fff7f05401094a60a390514622bbaa5649a8fb1b5f85f938e856192dac34e5c","size_bytes":8891},{"path":"tuner/execution/foundation_v2/executors.py","sha256":"27a428368c7aa4b9b502ae8028de03b48b817ea9d237a08dd112e7a6afc20454","size_bytes":5331},{"path":"tuner/execution/foundation_v2/identities.py","sha256":"720ae3938627f075c181eb5a125d118df87c9d6f7f24869107c74df9fbc44388","size_bytes":2794},{"path":"tuner/execution/foundation_v2/observations.py","sha256":"3642e4fd35478be5cb9a41df8f758ec4581d9f341f470bad1a4a2fbf8093636c","size_bytes":2566},{"path":"tuner/execution/foundation_v2/operations.py","sha256":"0f3c12d916a71a1fe20017b5ff7e99676c0acbf8bae7fac903d629c1aa3a9e6d","size_bytes":2259},{"path":"tuner/execution/foundation_v2/preparation.py","sha256":"09883f2455e521a7baa9b30fbb81518aa33ab2845392009d87cb54606ff80f1d","size_bytes":3575},{"path":"tuner/execution/foundation_v2/receipts.py","sha256":"c327476257236209ea1e29258fa55d90859bb262d27c132870d21399dfb5f626","size_bytes":15339},{"path":"tuner/execution/foundation_v2/references.py","sha256":"53d8e2070395a4c7f5c5a9361532f7a78de714003c760b55a23366394561b37f","size_bytes":3336},{"path":"tuner/execution/foundation_v2/repository.py","sha256":"d311b21fe8f2ed4b85d50ffa55fca370fe6452841d7a0189c5d56b841f69ef7f","size_bytes":57985},{"path":"tuner/execution/lifecycle.py","sha256":"4bc878038fc2384c0213a5f5c887aca8b15a67ed46b11e1acae1e2ecf0e97793","size_bytes":17660},{"path":"tuner/execution/operation.py","sha256":"9f86c585fc7ac3f7b2689a4701d598e32a382c3d1e0b87a7732ef35235568504","size_bytes":13702},{"path":"tuner/execution/providers/__init__.py","sha256":"8848731f5d1877c77ea8fcb66a82b85196aeafc06ba0e7463eca291ec1a5f41d","size_bytes":252},{"path":"tuner/execution/providers/contracts.py","sha256":"a21f22c8d373037d6426cd94dda48d7252438a08e115120188980dcaa34d6361","size_bytes":11506},{"path":"tuner/execution/providers/modal/__init__.py","sha256":"0f4e16bed9b4d05c59a5eef1d0ddc061720d5817a68a2bbe1073b20a19c27319","size_bytes":104},{"path":"tuner/execution/providers/modal/binding.py","sha256":"2d2acb7685798f487988b7690c1dbaf6ddc49e34ed8cd3600add438e17fc9ad5","size_bytes":1810},{"path":"tuner/execution/providers/modal/config.py","sha256":"bfa32ed26f3b8ece5e89e1a3524cb81e85ae3240e72ce93430ffb82dbb02f525","size_bytes":20788},{"path":"tuner/execution/providers/modal/contracts.py","sha256":"249e75a9ad13b93c65ac95cb100dc4fe24ed4c59674770832af3d5a9c4112dd2","size_bytes":6449},{"path":"tuner/execution/providers/modal/control.py","sha256":"19e05126196be5009c2cedac36a39008e82ca0bd17c9ec461f68f6ef26ece750","size_bytes":10359},{"path":"tuner/execution/providers/modal/coordinator_adapter.py","sha256":"619ba5e4e10f1a26ad4c154490da9328f9863581df02bf768e48e045ba46e413","size_bytes":13178},{"path":"tuner/execution/providers/modal/coordinator_binding.py","sha256":"e9267c37dce7273ffcad5f1db5332e205ba5282a9fbad4a647581e23c08b821e","size_bytes":4125},{"path":"tuner/execution/providers/modal/coordinator_bundle.py","sha256":"5d8a10d2c4a0dce83174eeca202c377730820ced6945be6f7717e3a0cfd91eb0","size_bytes":31316},{"path":"tuner/execution/providers/modal/coordinator_dispatch.py","sha256":"8e5bc475c6b7d15345e22e1f8cebdabcffab5a01ffefb7d4acbfb073be02b49f","size_bytes":10571},{"path":"tuner/execution/providers/modal/coordinator_launch.py","sha256":"5975bee2d73dd09e02008ede368ebc8f03325815e8bcfd4c093736214d5f0956","size_bytes":10982},{"path":"tuner/execution/providers/modal/coordinator_preflight.py","sha256":"f452e906d37fa9dba932e9df46f829447c95ec276191e34ec3e46831c89da875","size_bytes":13746},{"path":"tuner/execution/providers/modal/coordinator_reader.py","sha256":"92a1c21725587a88435d30b6b687f63711a12ad4c77345f0776521d6503103f4","size_bytes":28524},{"path":"tuner/execution/providers/modal/coordinator_staging.py","sha256":"de47b688e859ab96a612c01b1c7869d814e40b83ae81dcd7f2b1132e18c794f0","size_bytes":20880},{"path":"tuner/execution/providers/modal/coordinator_submit_preparation.py","sha256":"28814779c7f086016ae3ff617e0f1d4cbd8b52f518d3d889a905ea03d70456ec","size_bytes":5213},{"path":"tuner/execution/providers/modal/coordinator_wire.py","sha256":"07d46b92b33e30cb45bb6b1139ce4f80afdb09e1a17a3f3c2526ddcfae6983c9","size_bytes":12998},{"path":"tuner/execution/providers/modal/deployment_identity.py","sha256":"1b7007e5a9c11abdfe0ad4f106f742faa0ee556921b4017d3b48cb772bd0da4f","size_bytes":1196},{"path":"tuner/execution/providers/modal/deployment_v1.py","sha256":"579ace8adbddfe30e09d5a39df9926a54b111a139c704fc7efc7cebf533acf94","size_bytes":3628},{"path":"tuner/execution/providers/modal/facade.py","sha256":"55b89b3d0faff6238eeead068d6963d26f27749d725d2f2b8ae32449b348e32d","size_bytes":17704},{"path":"tuner/execution/providers/modal/inference-dependencies.lock","sha256":"ca4e3f0d7fa06533ad1127d84b539279791e37f07dc62cc81fbf0edd3a59de88","size_bytes":8458},{"path":"tuner/execution/providers/modal/inference-worker-closure.json","sha256":"ff03c7734b895c0dba5805cdc7dbaa85bd4a2b95c046b4e68e0fda7e6d02ddcc","size_bytes":18118},{"path":"tuner/execution/providers/modal/inference_artifacts.py","sha256":"5816f0c5662de8edad752554ba149d9527ced40ebdceeccc993c136ab099bff3","size_bytes":14973},{"path":"tuner/execution/providers/modal/inference_binding.py","sha256":"f2e8c4f1d8fc1f5e70702c88cbfff044a1f4f569e06b9a31c33a0cb9aaaf5894","size_bytes":23363},{"path":"tuner/execution/providers/modal/inference_bootstrap.py","sha256":"db2f09c1dfb2aee667b46fbb6566be8b2922d8e762896e5577d5c5078ad7d122","size_bytes":12546},{"path":"tuner/execution/providers/modal/inference_channel.py","sha256":"23492b4c7f48d86d23fb21ffd3902ac3f48bae381c30ea4759c803577911592e","size_bytes":13438},{"path":"tuner/execution/providers/modal/inference_commands.py","sha256":"1fa2dd667cb3b33e012d37b0688bfe08fc7a7cec52230a202f1f9ddda70110c5","size_bytes":4977},{"path":"tuner/execution/providers/modal/inference_entrypoint.py","sha256":"6ec8bdcbbf6d87fb5fc02a2c42d2c0f344ea965ccc4c0d94a630055c6e779991","size_bytes":10873},{"path":"tuner/execution/providers/modal/inference_model.py","sha256":"c08e1a35bb71a11c3466aedfbd8d3de87cf32d6cbd85820c3dcde2fefd14fd65","size_bytes":5935},{"path":"tuner/execution/providers/modal/inference_preparation.py","sha256":"43a1c883a8e5b2f25364e3a20f59d92a852af15fda0c9d506c213c0bf185fc32","size_bytes":49436},{"path":"tuner/execution/providers/modal/inference_runtime.py","sha256":"f8ba0d5744f90ca95e1fea3e5faf293e153a91047ffb09596f0305e1339d56ee","size_bytes":19283},{"path":"tuner/execution/providers/modal/inference_wire.py","sha256":"0492c10c6a384596bba04b3b409dc34f1f1a4afa88b7321d0d1a6fac5e29a1fb","size_bytes":20150},{"path":"tuner/execution/providers/modal/inference_worker.py","sha256":"cfb27e938c60757ef08c80b918fb522012c84918e7a0cd41a63de2cb2486cb87","size_bytes":15657},{"path":"tuner/execution/providers/modal/inference_workload.py","sha256":"4eaf399485e76829a98f57907c012658ce41d02745978827f21262ce2be94ff8","size_bytes":20421},{"path":"tuner/execution/providers/modal/manifest.py","sha256":"fc2ab4dcc46d136a681bf124d7a9fc3e5deedeeb3641d798fd207cdb62bb506f","size_bytes":8579},{"path":"tuner/execution/providers/modal/model_snapshot.py","sha256":"9d4762b821b2944a8a62932d0aec105c2f6920d64bf4ceed6907e5458799e337","size_bytes":14166},{"path":"tuner/execution/providers/modal/mounted_io.py","sha256":"e49da2e749d3be1ca19fdc7f581b29fb6d1e748a86fffcf0fde2be5cdc1a0d5b","size_bytes":13076},{"path":"tuner/execution/providers/modal/prepared_input.py","sha256":"c16cef5cfcc73ab9a7826977b26e18912ac46409d91b9eae165893ed265896ac","size_bytes":8597},{"path":"tuner/execution/providers/modal/resolution.py","sha256":"5c7268aa644103ae0712b03ed55918527d6e2ed6f4120ef169866b0ffd1cbf6f","size_bytes":30179},{"path":"tuner/execution/providers/modal/runtime.py","sha256":"ada13733021a6dab19331da80076df630055abc5bddc192a34a5cda65ac718c4","size_bytes":14810},{"path":"tuner/execution/providers/modal/worker_ports.py","sha256":"adf334368dbb75e40177078cd32aedc445cd9dde8b5ece2401319d472dbccdb0","size_bytes":2928},{"path":"tuner/execution/registry.py","sha256":"0cf424ff4b6bd628bae86b347f00a10acce8793cc41afc2ff1f3abbb738aec40","size_bytes":1515},{"path":"tuner/execution/service.py","sha256":"a85e31e8a54479764463633a9c721b9af3afe38710f85efa35b9a5744ad33e4c","size_bytes":4514},{"path":"tuner/inference/__init__.py","sha256":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","size_bytes":0},{"path":"tuner/inference/chat_session.py","sha256":"b95a829f834406a0a0c152f9af713225fe205a9225ad7b0f959b1ba801dbcac2","size_bytes":22355},{"path":"tuner/inference/owned_process.py","sha256":"f4cbb4e1c2305615b2c127e1103f60b1e17e955fe20a2a847b1d88b7ba8c4c98","size_bytes":8832},{"path":"tuner/inference/retrieved_model.py","sha256":"4a8cf2b2cbe4f03bf7c4d240c5e59a6ec6d20826c686b3a06c0bc1642fa234d8","size_bytes":31703},{"path":"tuner/inference/run_chat.py","sha256":"9263d325224bee4b9a0e7c16045e30766d9a2e2d2b550f0508040161687d3cfc","size_bytes":6228},{"path":"tuner/inference/serving_target.py","sha256":"50ec8054d1d6d08724d7a29183ca54a04d9c915d2c04274bdac6834764bdb800","size_bytes":12227},{"path":"tuner/inference/vllm_runtime.py","sha256":"fafbcfdeee78143a2760e5fbf29621897e9be014814eefb69f1167b188c296c1","size_bytes":26019},{"path":"tuner/project/__init__.py","sha256":"a858562f45452031f8a0e8ba4c9799f9784e09360a0258165dc3c4f89a46ca23","size_bytes":1513},{"path":"tuner/project/config_layers.py","sha256":"0e06ddfb8b92faa106d052990df34f199c06c298c3a3a2366cab218f2c1731f1","size_bytes":6714},{"path":"tuner/project/context.py","sha256":"147d08d5d6fcb3cf258ab347f070424e65e5c0c28e47ad1fd062e13cf56a8dd3","size_bytes":6518},{"path":"tuner/project/errors.py","sha256":"6f1f94cd0b4e068a0cdee82259de0aee09353345e93caeb05272097a5e717444","size_bytes":1511},{"path":"tuner/project/execution_source.py","sha256":"dc196dff10029c9a77b6d94fbfbcca45b39da4840bff40009b7152fd39d9622b","size_bytes":30595},{"path":"tuner/project/git_verification.py","sha256":"0cfe00560c704eddbe688a0c7156c12f9acd2f321046017f50eb5d9e3bb61710","size_bytes":9767},{"path":"tuner/project/manifest.py","sha256":"5506cf2ffb261988c0bbc5ffab1e466236bd10f503df5e805453f5ddf7d34be0","size_bytes":9498},{"path":"tuner/project/path_refs.py","sha256":"35e47de6ed1c6096ba45e7ea64aedcc0f79aeb9425acb5041b14afb9d258c955","size_bytes":6287},{"path":"tuner/project/secrets.py","sha256":"6f267060b0f24ab1517c4d4499fe54a993a77c2c78830c2d79415da871893818","size_bytes":4256},{"path":"tuner/project/source_bundle.py","sha256":"16368658db727680f74326603e2d28f3ae9b5e481f7626c7be1a8280b588ecec","size_bytes":28339},{"path":"tuner/runtime/__init__.py","sha256":"246429ce58a07dabeb05bf17967d3a7862014d114ee995a7419adcb27227286c","size_bytes":3566},{"path":"tuner/runtime/artifacts.py","sha256":"c9eaefd6f6805af11d997256e14392c0c42173b852d94bfab90bcb20a1ae2cd2","size_bytes":6913},{"path":"tuner/runtime/dispatch.py","sha256":"4c41faf87d7e620bdc281490bf3725550b83dacca8b15ea47bab253f669cdd8e","size_bytes":35463},{"path":"tuner/runtime/offline_sft_worker.py","sha256":"248f258254bb9c7021eed2ee0ff32d9633a021e656c10efb15825e7f6aaa9366","size_bytes":32592},{"path":"tuner/runtime/releases.py","sha256":"c0aa7ba37e8d35287fdd480cf0c76053a554e546c40c8bf36f5cf05d1a96d22b","size_bytes":52535},{"path":"tuner/runtime/verification.py","sha256":"4c2314b20cbf7937a3f24aa90676000b9e21f0ab6701b2c1225a53e5596bf4a0","size_bytes":53856},{"path":"tuner/training/__init__.py","sha256":"23ca59d6203276c6160e0bb57dbd5c43cf385c6aec60b4e3e08b93d1d501cb97","size_bytes":2995},{"path":"tuner/training/contracts.py","sha256":"91c4d66d9810cc0471981c3d4ef4b54feaed5b92eb41a468f31201f8111303b5","size_bytes":21504},{"path":"tuner/training/coordinator_material.py","sha256":"c563aa3b1fbd940ddd3b8e56e4f33de70675cb831e938175f40debc0645fd9c7","size_bytes":12950},{"path":"tuner/training/grpo_compilation.py","sha256":"c6268486375bbc16f1888bb43d07b93a8de280d000a774914d59d7fd05ca5cc0","size_bytes":8998},{"path":"tuner/training/methods/__init__.py","sha256":"99db689246273707ae3df5087859ebb510e84b664f9f173c0d5827abc3e6a3ea","size_bytes":361},{"path":"tuner/training/methods/sft.py","sha256":"b501f9c214dec4b0b1550543281c25aba8dbb102604df253c641c85f7cae587b","size_bytes":9997},{"path":"tuner/training/packaged_compilation.py","sha256":"477caede6902fc587af680889b93fd9e0e42493d1a6653f3cd7413a9c7cadde8","size_bytes":9413},{"path":"tuner/training/packaged_grpo_compilation.py","sha256":"d186a05ffb8cc756e782b2bb9a3f66e925779104b70045bd56a5592846fec524","size_bytes":8722},{"path":"tuner/training/post_training.py","sha256":"ba8c2ea3fe49a647d502c6a874a44658cc2e575d4cd5729966102b0ebb973a88","size_bytes":7971},{"path":"tuner/training/recipes.py","sha256":"bb844ed4883f00ccea9936eebad50c466e6e4bc148e88aa680d93121edf67dcc","size_bytes":6300}],"worker_closure_manifest_path":"tuner/execution/providers/modal/inference-worker-closure.json"} +{"base_registry_reference":"docker.io/vllm/vllm-openai@sha256:116aa00ee0b68855616a56e1d7e1ae937e591a8bd6969ee45cbcedb246ddf355","dependency_lock_path":"tuner/execution/providers/modal/inference-dependencies.lock","distributions":{"accelerate":"1.13.0","aiofile":"3.9.0","aiofiles":"25.1.0","aiohappyeyeballs":"2.6.1","aiohttp":"3.13.3","aiosignal":"1.4.0","annotated-doc":"0.0.4","annotated-types":"0.7.0","anthropic":"0.84.0","anyio":"4.12.1","apache-tvm-ffi":"0.1.9","astor":"0.8.1","attrs":"25.4.0","awscrt":"0.31.3","bitsandbytes":"0.49.2","blake3":"1.0.8","boto3":"1.42.65","botocore":"1.42.65","cachetools":"7.0.5","caio":"0.9.25","cbor2":"5.8.0","certifi":"2026.2.25","cffi":"2.0.0","charset-normalizer":"3.4.5","click":"8.3.1","cloudpickle":"3.1.2","compressed-tensors":"0.13.0","cryptography":"46.0.5","cuda-bindings":"12.9.4","cuda-pathfinder":"1.4.2","cuda-python":"12.9.4","cufile-python":"0.2.0","cupy-cuda12x":"14.0.1","deep-ep":"1.2.1+73b6ea4","deep-gemm":"2.3.0+477618c","depyf":"0.20.0","dill":"0.4.1","diskcache":"5.6.3","distro":"1.9.0","dnspython":"2.8.0","docstring-parser":"0.17.0","einops":"0.8.2","email-validator":"2.3.0","fastapi":"0.135.1","fastapi-cli":"0.0.24","fastapi-cloud-cli":"0.14.1","fastar":"0.8.0","filelock":"3.25.1","flashinfer-cubin":"0.6.4","flashinfer-jit-cache":"0.6.4+cu129","flashinfer-python":"0.6.4","frozenlist":"1.8.0","fsspec":"2026.2.0","gguf":"0.18.0","google-api-core":"2.30.0","google-auth":"2.49.0","google-cloud-core":"2.5.0","google-cloud-storage":"3.9.0","google-crc32c":"1.8.0","google-resumable-media":"2.8.0","googleapis-common-protos":"1.73.0","grpcio":"1.78.0","grpcio-reflection":"1.78.0","grpclib":"0.4.9","h11":"0.16.0","h2":"4.4.1","hf-transfer":"0.1.9","hf-xet":"1.3.2","hpack":"4.2.0","httpcore":"1.0.9","httptools":"0.7.1","httpx":"0.28.1","httpx-sse":"0.4.3","huggingface-hub":"0.36.2","humanize":"4.15.0","hyperframe":"6.1.0","idna":"3.11","ijson":"3.5.0","importlib-metadata":"8.7.1","interegular":"0.3.3","jinja2":"3.1.6","jiter":"0.13.0","jmespath":"1.1.0","jsonschema":"4.26.0","jsonschema-specifications":"2025.9.1","kaldi-native-fbank":"1.22.3","lark":"1.2.2","llguidance":"1.3.0","llvmlite":"0.44.0","lm-format-enforcer":"0.11.3","lmcache":"0.4.0","loguru":"0.7.3","markdown-it-py":"4.0.0","markupsafe":"3.0.3","mcp":"1.26.0","mdurl":"0.1.2","mistral-common":"1.9.1","modal":"1.5.4","model-hosting-container-standards":"0.1.13","modelscope":"1.34.0","mooncake-transfer-engine":"0.3.9","mpmath":"1.3.0","msgpack":"1.1.2","msgspec":"0.20.0","multidict":"6.7.1","networkx":"3.6.1","ninja":"1.13.0","nixl":"0.9.0","nixl-cu12":"0.10.1","numba":"0.61.2","numpy":"2.2.6","nvidia-cublas-cu12":"12.9.1.4","nvidia-cuda-cupti-cu12":"12.9.79","nvidia-cuda-nvrtc-cu12":"12.9.86","nvidia-cuda-runtime-cu12":"12.9.79","nvidia-cudnn-cu12":"9.10.2.21","nvidia-cudnn-frontend":"1.18.0","nvidia-cufft-cu12":"11.4.1.4","nvidia-cufile-cu12":"1.14.1.1","nvidia-curand-cu12":"10.3.10.19","nvidia-cusolver-cu12":"11.7.5.82","nvidia-cusparse-cu12":"12.5.10.65","nvidia-cusparselt-cu12":"0.7.1","nvidia-cutlass-dsl":"4.4.1","nvidia-cutlass-dsl-libs-base":"4.4.1","nvidia-ml-py":"13.590.48","nvidia-nccl-cu12":"2.27.5","nvidia-nvjitlink-cu12":"12.9.86","nvidia-nvshmem-cu12":"3.4.5","nvidia-nvtx-cu12":"12.9.79","nvtx":"0.2.14","openai":"2.24.0","openai-harmony":"0.0.8","opencv-python-headless":"4.13.0.92","opentelemetry-api":"1.40.0","opentelemetry-exporter-otlp":"1.40.0","opentelemetry-exporter-otlp-proto-common":"1.40.0","opentelemetry-exporter-otlp-proto-grpc":"1.40.0","opentelemetry-exporter-otlp-proto-http":"1.40.0","opentelemetry-proto":"1.40.0","opentelemetry-sdk":"1.40.0","opentelemetry-semantic-conventions":"0.61b0","opentelemetry-semantic-conventions-ai":"0.4.15","outlines-core":"0.2.11","packaging":"26.0","partial-json-parser":"0.2.1.1.post7","pillow":"12.1.1","pip":"26.0.1","prometheus-client":"0.24.1","prometheus-fastapi-instrumentator":"7.1.0","propcache":"0.4.1","proto-plus":"1.27.1","protobuf":"6.33.5","psutil":"7.2.2","py-cpuinfo":"9.0.0","pyasn1":"0.6.2","pyasn1-modules":"0.4.2","pybase64":"1.4.3","pycountry":"26.2.16","pycparser":"3.0","pydantic":"2.12.5","pydantic-core":"2.41.5","pydantic-extra-types":"2.11.0","pydantic-settings":"2.13.1","pygments":"2.19.2","pyjwt":"2.11.0","python-dateutil":"2.9.0.post0","python-dotenv":"1.2.2","python-json-logger":"4.0.0","python-multipart":"0.0.22","pyyaml":"6.0.3","pyzmq":"27.1.0","quack-kernels":"0.3.2","ray":"2.54.0","redis":"7.3.0","referencing":"0.37.0","regex":"2026.2.28","requests":"2.32.5","rich":"14.3.3","rich-toolkit":"0.19.7","rignore":"0.7.6","rpds-py":"0.30.0","rsa":"4.9.1","runai-model-streamer":"0.15.7","runai-model-streamer-gcs":"0.15.7","runai-model-streamer-s3":"0.15.7","s3transfer":"0.16.0","safetensors":"0.7.0","sentencepiece":"0.2.1","sentry-sdk":"2.54.0","setproctitle":"1.3.7","setuptools":"80.10.2","setuptools-scm":"9.2.2","shellingham":"1.5.4","six":"1.17.0","sniffio":"1.3.1","sortedcontainers":"2.4.0","sse-starlette":"3.3.2","starlette":"0.52.1","supervisor":"4.3.0","sympy":"1.14.0","synaptic-tuner":"1.1.0","synchronicity":"0.12.5","tabulate":"0.10.0","tiktoken":"0.12.0","timm":"1.0.25","tokenizers":"0.22.2","toml":"0.10.2","torch":"2.10.0+cu129","torch-c-dlpack-ext":"0.1.5","torchaudio":"2.10.0+cu129","torchvision":"0.25.0+cu129","tqdm":"4.67.3","transformers":"4.57.6","triton":"3.6.0","typer":"0.24.1","types-certifi":"2021.10.8.3","types-toml":"0.10.8.20260518","typing-extensions":"4.15.0","typing-inspection":"0.4.2","urllib3":"2.6.3","uv":"0.10.9","uvicorn":"0.41.0","uvloop":"0.22.1","vllm":"0.17.1","watchfiles":"1.1.1","websockets":"16.0","xgrammar":"0.1.29","yarl":"1.23.0","zipp":"3.23.0"},"python":{"executable":"/opt/synaptic-inference/bin/python","executable_sha256":"185be5c01624944b5c422a8d1e4995a7df010036a99c9f9d84a80a09ae1bd72d","implementation":"cpython","version":"3.12.13"},"schema_version":"synaptic-modal-inference-runtime-lock/v1","sdk_version":"1.5.4","source_inventory":[{"path":"Evaluator/__init__.py","sha256":"d1b5aea793a95c47618d4b2469a8bae0b14fd11f81501c1d71546469a39a5cbd","size_bytes":3061},{"path":"Evaluator/base_client.py","sha256":"ac7e77909bdf161a4fd9b1ad2e1e2cfe085ec31f36baa6b19f8976f7b2358cbd","size_bytes":19362},{"path":"Evaluator/config.py","sha256":"94fbe8e198d065141ff9ad002b0a6e26e56d865fc1e3a3fb23479a8e69d09303","size_bytes":21956},{"path":"Evaluator/enums.py","sha256":"2ced9426d860e9ec410e376d9cf853d75e8e111e78dd0bee31e25bee295e4854","size_bytes":3371},{"path":"Evaluator/openai_compat_client.py","sha256":"6ac6211185d2c57d3b31022d31478e3253046fd6cb4de1903d8d0b209bad3a79","size_bytes":5174},{"path":"Evaluator/protocols.py","sha256":"239fe049b5fe4c9e9861aede487feac1f06a7642891378a1ccfba444072ad706","size_bytes":4560},{"path":"Evaluator/verified_vllm_chat.py","sha256":"f130e47d661aa77d68c104084fafe9e5df3af88684ef5a3139f4d53ff2e32465","size_bytes":5916},{"path":"Evaluator/vllm_client.py","sha256":"4e88d2055dc1b32cc22a1393cdf038ccd76d91d915a8be0774b0a8ec87bd4d1e","size_bytes":1995},{"path":"synaptic_tuner/__init__.py","sha256":"2462ff4ec439b77eb1ecfa901c1d8294628c853e9bbdf872de73dce9bfc0ae4d","size_bytes":134},{"path":"synaptic_tuner/_version.py","sha256":"49fa86b62849d537e7e131445f272650ed77760db5f218f2113155ab60caf644","size_bytes":96},{"path":"synaptic_tuner/api/__init__.py","sha256":"25c68ed273bf1e6b158021b98f8607276d980540a98b56a07801dccf9d6596a8","size_bytes":67},{"path":"synaptic_tuner/api/v1/__init__.py","sha256":"4bc30420b18b3932183dcd0485892fa22f4f8e474ca3bb9b5e08adf3ae0fde3d","size_bytes":16893},{"path":"synaptic_tuner/api/v1/_contract.py","sha256":"dcd6fc1ebdc75fbfd74ad4c6c96837b73209081258353557fedaaca76164673a","size_bytes":6702},{"path":"synaptic_tuner/api/v1/_timestamps.py","sha256":"2b2ad7183f62bf1d42c4b9144d0315fe90c0e601649fb37f7778d017c632935d","size_bytes":698},{"path":"synaptic_tuner/api/v1/planning.py","sha256":"e715b105a513fa56cfa510416e725471f8f3100cb34257be74bef3e40ca78460","size_bytes":9569},{"path":"synaptic_tuner/api/v1/providers.py","sha256":"eca5efd5a6dee453543ae338ffddf9451d34ce1743e4ab4bde024b47be571ec0","size_bytes":4940},{"path":"synaptic_tuner/api/v1/results.py","sha256":"65e530325c92c7afa18e3ca5f2ad9a11baf7d590221f08c33badfbf2d5f2c7b2","size_bytes":2236},{"path":"synaptic_tuner/api/v1/runs_facade.py","sha256":"bf6155726c99cfc5574385009e20f4f65b69a9ad4b96e33cf7065263d1d4c455","size_bytes":23572},{"path":"synaptic_tuner/api/v1/training_facade.py","sha256":"bf094d0e152f5861bf8a8ff51962e0700123d9b43ed91d914b9e78bbea6facd9","size_bytes":11118},{"path":"synaptic_tuner/api/v1/training_input.py","sha256":"209326325f8796ab7d038878e92def2c5521d7d98b7b9cc4dd1766d2e4f68c9d","size_bytes":38650},{"path":"tuner/__init__.py","sha256":"920cf8a0ca993a712fbacf734621e1fdf14fdd2a7203701db120dc41e4815e7b","size_bytes":2666},{"path":"tuner/cloud/__init__.py","sha256":"a9abb71aa9222c511524d3cc49b1cfe00f8938a6cb7c61792cf6135a6b82208b","size_bytes":3692},{"path":"tuner/cloud/runtime_layout.py","sha256":"2a4cd1f5fc63d95bb3f0b5bc4f44e616bb0308d9509aceb198943953746ff705","size_bytes":9435},{"path":"tuner/execution/__init__.py","sha256":"8193dfcd30c68a7d8ad02af0fb24f1a8120fce2a709decd132ab1acb33113d65","size_bytes":535},{"path":"tuner/execution/_effect_executor.py","sha256":"7aa3c523d971486417fe735c62a5cb3fe7e0698caa514d0da047293ad6b3e9cd","size_bytes":500},{"path":"tuner/execution/broker.py","sha256":"c504bd734b41ef9785fe5fa591966bb0b0bce3b43dcce0695b4ee0c142ca3d1e","size_bytes":6305},{"path":"tuner/execution/contracts.py","sha256":"82d90f742e4e33d8071e557b5a872e0158684d94db5debdfbb85411954e4e638","size_bytes":32907},{"path":"tuner/execution/coordinator_v1/__init__.py","sha256":"453718a1827cfb23e7ca0c908d3d256f62ca3e3fff8f75ed8a785f8336b17bbe","size_bytes":66},{"path":"tuner/execution/coordinator_v1/coordinator.py","sha256":"9bc8ecb60d472ad0b3c0d3192d8c73d88eb28dc43af55677fbdd4b3673b71ef9","size_bytes":47018},{"path":"tuner/execution/coordinator_v1/cursors.py","sha256":"5f7c899064dd26959aeed6b328b236b01f7ff549e2d29348bd45dc0480ede26b","size_bytes":9088},{"path":"tuner/execution/coordinator_v1/foundation.py","sha256":"4433c390075544df5516a5e213af4b4b697485ff7627f5a6dcace21a98029f94","size_bytes":12972},{"path":"tuner/execution/coordinator_v1/model.py","sha256":"1d2150d588f6b84f1b58913df398365a9491d6486a8a871bb2a22fa98cafc722","size_bytes":75837},{"path":"tuner/execution/coordinator_v1/ports.py","sha256":"7a028c32c3a3eab905bbc202248b4bf39411cd22e73fddacf5099b7d15dc3832","size_bytes":9343},{"path":"tuner/execution/coordinator_v1/state_machine.py","sha256":"e5e63db3a85fb9fc5b1d7bb5444691f88f308cfb0428842c0801b06d6f2fbb3a","size_bytes":50176},{"path":"tuner/execution/coordinator_v1/stores.py","sha256":"3a875f9e9d7d7d9899d5deba2f4d68cfada12bea499d5897592c9383ba2b16dc","size_bytes":39045},{"path":"tuner/execution/evidence.py","sha256":"a04cc6843a794e62a702608d28c8e82d0c57af8227b3cb9743cf56b9605442ca","size_bytes":4634},{"path":"tuner/execution/foundation_v2/__init__.py","sha256":"1651bf897879603a83e7824f60d369d52ac5d0308c96f988c24465f33112cd77","size_bytes":77},{"path":"tuner/execution/foundation_v2/authority.py","sha256":"a0eb5b3537a59c7b4d33e9b8617abd072376545d808f1989f562704dfa1ad024","size_bytes":9958},{"path":"tuner/execution/foundation_v2/canonical.py","sha256":"80620a6088d137bfacb210e000c406632bf569de66350628fb0e0aee612babfb","size_bytes":4047},{"path":"tuner/execution/foundation_v2/commands.py","sha256":"0fff7f05401094a60a390514622bbaa5649a8fb1b5f85f938e856192dac34e5c","size_bytes":8891},{"path":"tuner/execution/foundation_v2/executors.py","sha256":"27a428368c7aa4b9b502ae8028de03b48b817ea9d237a08dd112e7a6afc20454","size_bytes":5331},{"path":"tuner/execution/foundation_v2/identities.py","sha256":"720ae3938627f075c181eb5a125d118df87c9d6f7f24869107c74df9fbc44388","size_bytes":2794},{"path":"tuner/execution/foundation_v2/observations.py","sha256":"3642e4fd35478be5cb9a41df8f758ec4581d9f341f470bad1a4a2fbf8093636c","size_bytes":2566},{"path":"tuner/execution/foundation_v2/operations.py","sha256":"0f3c12d916a71a1fe20017b5ff7e99676c0acbf8bae7fac903d629c1aa3a9e6d","size_bytes":2259},{"path":"tuner/execution/foundation_v2/preparation.py","sha256":"09883f2455e521a7baa9b30fbb81518aa33ab2845392009d87cb54606ff80f1d","size_bytes":3575},{"path":"tuner/execution/foundation_v2/receipts.py","sha256":"c327476257236209ea1e29258fa55d90859bb262d27c132870d21399dfb5f626","size_bytes":15339},{"path":"tuner/execution/foundation_v2/references.py","sha256":"53d8e2070395a4c7f5c5a9361532f7a78de714003c760b55a23366394561b37f","size_bytes":3336},{"path":"tuner/execution/foundation_v2/repository.py","sha256":"d311b21fe8f2ed4b85d50ffa55fca370fe6452841d7a0189c5d56b841f69ef7f","size_bytes":57985},{"path":"tuner/execution/lifecycle.py","sha256":"4bc878038fc2384c0213a5f5c887aca8b15a67ed46b11e1acae1e2ecf0e97793","size_bytes":17660},{"path":"tuner/execution/operation.py","sha256":"9f86c585fc7ac3f7b2689a4701d598e32a382c3d1e0b87a7732ef35235568504","size_bytes":13702},{"path":"tuner/execution/providers/__init__.py","sha256":"8848731f5d1877c77ea8fcb66a82b85196aeafc06ba0e7463eca291ec1a5f41d","size_bytes":252},{"path":"tuner/execution/providers/contracts.py","sha256":"a21f22c8d373037d6426cd94dda48d7252438a08e115120188980dcaa34d6361","size_bytes":11506},{"path":"tuner/execution/providers/modal/__init__.py","sha256":"0f4e16bed9b4d05c59a5eef1d0ddc061720d5817a68a2bbe1073b20a19c27319","size_bytes":104},{"path":"tuner/execution/providers/modal/binding.py","sha256":"2d2acb7685798f487988b7690c1dbaf6ddc49e34ed8cd3600add438e17fc9ad5","size_bytes":1810},{"path":"tuner/execution/providers/modal/config.py","sha256":"bfa32ed26f3b8ece5e89e1a3524cb81e85ae3240e72ce93430ffb82dbb02f525","size_bytes":20788},{"path":"tuner/execution/providers/modal/contracts.py","sha256":"249e75a9ad13b93c65ac95cb100dc4fe24ed4c59674770832af3d5a9c4112dd2","size_bytes":6449},{"path":"tuner/execution/providers/modal/control.py","sha256":"19e05126196be5009c2cedac36a39008e82ca0bd17c9ec461f68f6ef26ece750","size_bytes":10359},{"path":"tuner/execution/providers/modal/coordinator_adapter.py","sha256":"619ba5e4e10f1a26ad4c154490da9328f9863581df02bf768e48e045ba46e413","size_bytes":13178},{"path":"tuner/execution/providers/modal/coordinator_binding.py","sha256":"e9267c37dce7273ffcad5f1db5332e205ba5282a9fbad4a647581e23c08b821e","size_bytes":4125},{"path":"tuner/execution/providers/modal/coordinator_bundle.py","sha256":"5d8a10d2c4a0dce83174eeca202c377730820ced6945be6f7717e3a0cfd91eb0","size_bytes":31316},{"path":"tuner/execution/providers/modal/coordinator_dispatch.py","sha256":"8e5bc475c6b7d15345e22e1f8cebdabcffab5a01ffefb7d4acbfb073be02b49f","size_bytes":10571},{"path":"tuner/execution/providers/modal/coordinator_launch.py","sha256":"5975bee2d73dd09e02008ede368ebc8f03325815e8bcfd4c093736214d5f0956","size_bytes":10982},{"path":"tuner/execution/providers/modal/coordinator_preflight.py","sha256":"f452e906d37fa9dba932e9df46f829447c95ec276191e34ec3e46831c89da875","size_bytes":13746},{"path":"tuner/execution/providers/modal/coordinator_reader.py","sha256":"92a1c21725587a88435d30b6b687f63711a12ad4c77345f0776521d6503103f4","size_bytes":28524},{"path":"tuner/execution/providers/modal/coordinator_staging.py","sha256":"de47b688e859ab96a612c01b1c7869d814e40b83ae81dcd7f2b1132e18c794f0","size_bytes":20880},{"path":"tuner/execution/providers/modal/coordinator_submit_preparation.py","sha256":"28814779c7f086016ae3ff617e0f1d4cbd8b52f518d3d889a905ea03d70456ec","size_bytes":5213},{"path":"tuner/execution/providers/modal/coordinator_wire.py","sha256":"07d46b92b33e30cb45bb6b1139ce4f80afdb09e1a17a3f3c2526ddcfae6983c9","size_bytes":12998},{"path":"tuner/execution/providers/modal/deployment_identity.py","sha256":"1b7007e5a9c11abdfe0ad4f106f742faa0ee556921b4017d3b48cb772bd0da4f","size_bytes":1196},{"path":"tuner/execution/providers/modal/deployment_v1.py","sha256":"579ace8adbddfe30e09d5a39df9926a54b111a139c704fc7efc7cebf533acf94","size_bytes":3628},{"path":"tuner/execution/providers/modal/facade.py","sha256":"55b89b3d0faff6238eeead068d6963d26f27749d725d2f2b8ae32449b348e32d","size_bytes":17704},{"path":"tuner/execution/providers/modal/inference-dependencies.lock","sha256":"ca4e3f0d7fa06533ad1127d84b539279791e37f07dc62cc81fbf0edd3a59de88","size_bytes":8458},{"path":"tuner/execution/providers/modal/inference-worker-closure.json","sha256":"cf21d2b602504a7ccccd907bf8358d485501dc6798eae8a0779b70de5c305fd6","size_bytes":18118},{"path":"tuner/execution/providers/modal/inference_artifacts.py","sha256":"5816f0c5662de8edad752554ba149d9527ced40ebdceeccc993c136ab099bff3","size_bytes":14973},{"path":"tuner/execution/providers/modal/inference_binding.py","sha256":"f2e8c4f1d8fc1f5e70702c88cbfff044a1f4f569e06b9a31c33a0cb9aaaf5894","size_bytes":23363},{"path":"tuner/execution/providers/modal/inference_bootstrap.py","sha256":"db2f09c1dfb2aee667b46fbb6566be8b2922d8e762896e5577d5c5078ad7d122","size_bytes":12546},{"path":"tuner/execution/providers/modal/inference_channel.py","sha256":"23492b4c7f48d86d23fb21ffd3902ac3f48bae381c30ea4759c803577911592e","size_bytes":13438},{"path":"tuner/execution/providers/modal/inference_commands.py","sha256":"1fa2dd667cb3b33e012d37b0688bfe08fc7a7cec52230a202f1f9ddda70110c5","size_bytes":4977},{"path":"tuner/execution/providers/modal/inference_entrypoint.py","sha256":"6ec8bdcbbf6d87fb5fc02a2c42d2c0f344ea965ccc4c0d94a630055c6e779991","size_bytes":10873},{"path":"tuner/execution/providers/modal/inference_model.py","sha256":"c08e1a35bb71a11c3466aedfbd8d3de87cf32d6cbd85820c3dcde2fefd14fd65","size_bytes":5935},{"path":"tuner/execution/providers/modal/inference_preparation.py","sha256":"43a1c883a8e5b2f25364e3a20f59d92a852af15fda0c9d506c213c0bf185fc32","size_bytes":49436},{"path":"tuner/execution/providers/modal/inference_runtime.py","sha256":"f8ba0d5744f90ca95e1fea3e5faf293e153a91047ffb09596f0305e1339d56ee","size_bytes":19283},{"path":"tuner/execution/providers/modal/inference_wire.py","sha256":"0492c10c6a384596bba04b3b409dc34f1f1a4afa88b7321d0d1a6fac5e29a1fb","size_bytes":20150},{"path":"tuner/execution/providers/modal/inference_worker.py","sha256":"cfb27e938c60757ef08c80b918fb522012c84918e7a0cd41a63de2cb2486cb87","size_bytes":15657},{"path":"tuner/execution/providers/modal/inference_workload.py","sha256":"4eaf399485e76829a98f57907c012658ce41d02745978827f21262ce2be94ff8","size_bytes":20421},{"path":"tuner/execution/providers/modal/manifest.py","sha256":"fc2ab4dcc46d136a681bf124d7a9fc3e5deedeeb3641d798fd207cdb62bb506f","size_bytes":8579},{"path":"tuner/execution/providers/modal/model_snapshot.py","sha256":"9d4762b821b2944a8a62932d0aec105c2f6920d64bf4ceed6907e5458799e337","size_bytes":14166},{"path":"tuner/execution/providers/modal/mounted_io.py","sha256":"e49da2e749d3be1ca19fdc7f581b29fb6d1e748a86fffcf0fde2be5cdc1a0d5b","size_bytes":13076},{"path":"tuner/execution/providers/modal/prepared_input.py","sha256":"c16cef5cfcc73ab9a7826977b26e18912ac46409d91b9eae165893ed265896ac","size_bytes":8597},{"path":"tuner/execution/providers/modal/resolution.py","sha256":"5c7268aa644103ae0712b03ed55918527d6e2ed6f4120ef169866b0ffd1cbf6f","size_bytes":30179},{"path":"tuner/execution/providers/modal/runtime.py","sha256":"ada13733021a6dab19331da80076df630055abc5bddc192a34a5cda65ac718c4","size_bytes":14810},{"path":"tuner/execution/providers/modal/worker_ports.py","sha256":"adf334368dbb75e40177078cd32aedc445cd9dde8b5ece2401319d472dbccdb0","size_bytes":2928},{"path":"tuner/execution/registry.py","sha256":"0cf424ff4b6bd628bae86b347f00a10acce8793cc41afc2ff1f3abbb738aec40","size_bytes":1515},{"path":"tuner/execution/service.py","sha256":"a85e31e8a54479764463633a9c721b9af3afe38710f85efa35b9a5744ad33e4c","size_bytes":4514},{"path":"tuner/inference/__init__.py","sha256":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","size_bytes":0},{"path":"tuner/inference/chat_session.py","sha256":"b95a829f834406a0a0c152f9af713225fe205a9225ad7b0f959b1ba801dbcac2","size_bytes":22355},{"path":"tuner/inference/owned_process.py","sha256":"f4cbb4e1c2305615b2c127e1103f60b1e17e955fe20a2a847b1d88b7ba8c4c98","size_bytes":8832},{"path":"tuner/inference/retrieved_model.py","sha256":"4a8cf2b2cbe4f03bf7c4d240c5e59a6ec6d20826c686b3a06c0bc1642fa234d8","size_bytes":31703},{"path":"tuner/inference/run_chat.py","sha256":"9263d325224bee4b9a0e7c16045e30766d9a2e2d2b550f0508040161687d3cfc","size_bytes":6228},{"path":"tuner/inference/serving_target.py","sha256":"50ec8054d1d6d08724d7a29183ca54a04d9c915d2c04274bdac6834764bdb800","size_bytes":12227},{"path":"tuner/inference/vllm_runtime.py","sha256":"fafbcfdeee78143a2760e5fbf29621897e9be014814eefb69f1167b188c296c1","size_bytes":26019},{"path":"tuner/project/__init__.py","sha256":"a858562f45452031f8a0e8ba4c9799f9784e09360a0258165dc3c4f89a46ca23","size_bytes":1513},{"path":"tuner/project/config_layers.py","sha256":"0e06ddfb8b92faa106d052990df34f199c06c298c3a3a2366cab218f2c1731f1","size_bytes":6714},{"path":"tuner/project/context.py","sha256":"147d08d5d6fcb3cf258ab347f070424e65e5c0c28e47ad1fd062e13cf56a8dd3","size_bytes":6518},{"path":"tuner/project/errors.py","sha256":"6f1f94cd0b4e068a0cdee82259de0aee09353345e93caeb05272097a5e717444","size_bytes":1511},{"path":"tuner/project/execution_source.py","sha256":"dc196dff10029c9a77b6d94fbfbcca45b39da4840bff40009b7152fd39d9622b","size_bytes":30595},{"path":"tuner/project/git_verification.py","sha256":"0cfe00560c704eddbe688a0c7156c12f9acd2f321046017f50eb5d9e3bb61710","size_bytes":9767},{"path":"tuner/project/manifest.py","sha256":"5506cf2ffb261988c0bbc5ffab1e466236bd10f503df5e805453f5ddf7d34be0","size_bytes":9498},{"path":"tuner/project/path_refs.py","sha256":"35e47de6ed1c6096ba45e7ea64aedcc0f79aeb9425acb5041b14afb9d258c955","size_bytes":6287},{"path":"tuner/project/secrets.py","sha256":"6f267060b0f24ab1517c4d4499fe54a993a77c2c78830c2d79415da871893818","size_bytes":4256},{"path":"tuner/project/source_bundle.py","sha256":"16368658db727680f74326603e2d28f3ae9b5e481f7626c7be1a8280b588ecec","size_bytes":28339},{"path":"tuner/runtime/__init__.py","sha256":"246429ce58a07dabeb05bf17967d3a7862014d114ee995a7419adcb27227286c","size_bytes":3566},{"path":"tuner/runtime/artifacts.py","sha256":"c9eaefd6f6805af11d997256e14392c0c42173b852d94bfab90bcb20a1ae2cd2","size_bytes":6913},{"path":"tuner/runtime/dispatch.py","sha256":"4c41faf87d7e620bdc281490bf3725550b83dacca8b15ea47bab253f669cdd8e","size_bytes":35463},{"path":"tuner/runtime/offline_sft_worker.py","sha256":"2304eeb00667954510c34cc4aacaa818532a301fbeabf04c2fb165d6cd1d70b5","size_bytes":36343},{"path":"tuner/runtime/releases.py","sha256":"c0aa7ba37e8d35287fdd480cf0c76053a554e546c40c8bf36f5cf05d1a96d22b","size_bytes":52535},{"path":"tuner/runtime/verification.py","sha256":"4c2314b20cbf7937a3f24aa90676000b9e21f0ab6701b2c1225a53e5596bf4a0","size_bytes":53856},{"path":"tuner/training/__init__.py","sha256":"23ca59d6203276c6160e0bb57dbd5c43cf385c6aec60b4e3e08b93d1d501cb97","size_bytes":2995},{"path":"tuner/training/contracts.py","sha256":"91c4d66d9810cc0471981c3d4ef4b54feaed5b92eb41a468f31201f8111303b5","size_bytes":21504},{"path":"tuner/training/coordinator_material.py","sha256":"c563aa3b1fbd940ddd3b8e56e4f33de70675cb831e938175f40debc0645fd9c7","size_bytes":12950},{"path":"tuner/training/grpo_compilation.py","sha256":"c6268486375bbc16f1888bb43d07b93a8de280d000a774914d59d7fd05ca5cc0","size_bytes":8998},{"path":"tuner/training/methods/__init__.py","sha256":"99db689246273707ae3df5087859ebb510e84b664f9f173c0d5827abc3e6a3ea","size_bytes":361},{"path":"tuner/training/methods/sft.py","sha256":"b501f9c214dec4b0b1550543281c25aba8dbb102604df253c641c85f7cae587b","size_bytes":9997},{"path":"tuner/training/packaged_compilation.py","sha256":"477caede6902fc587af680889b93fd9e0e42493d1a6653f3cd7413a9c7cadde8","size_bytes":9413},{"path":"tuner/training/packaged_grpo_compilation.py","sha256":"d186a05ffb8cc756e782b2bb9a3f66e925779104b70045bd56a5592846fec524","size_bytes":8722},{"path":"tuner/training/post_training.py","sha256":"ba8c2ea3fe49a647d502c6a874a44658cc2e575d4cd5729966102b0ebb973a88","size_bytes":7971},{"path":"tuner/training/recipes.py","sha256":"bb844ed4883f00ccea9936eebad50c466e6e4bc148e88aa680d93121edf67dcc","size_bytes":6300}],"worker_closure_manifest_path":"tuner/execution/providers/modal/inference-worker-closure.json"} diff --git a/tuner/execution/providers/modal/inference-worker-closure.json b/tuner/execution/providers/modal/inference-worker-closure.json index 654e5e43..65c5551a 100644 --- a/tuner/execution/providers/modal/inference-worker-closure.json +++ b/tuner/execution/providers/modal/inference-worker-closure.json @@ -1 +1 @@ -{"closure_digest":"b993fc4edb2b8ecbfbc47b35c87f227ea5232b7ac4e48b9512ae0ec4bcc88c64","entrypoint":"tuner/execution/providers/modal/inference_bootstrap.py","member_count":124,"members":[{"path":"Evaluator/__init__.py","sha256":"d1b5aea793a95c47618d4b2469a8bae0b14fd11f81501c1d71546469a39a5cbd","size_bytes":3061},{"path":"Evaluator/base_client.py","sha256":"ac7e77909bdf161a4fd9b1ad2e1e2cfe085ec31f36baa6b19f8976f7b2358cbd","size_bytes":19362},{"path":"Evaluator/config.py","sha256":"94fbe8e198d065141ff9ad002b0a6e26e56d865fc1e3a3fb23479a8e69d09303","size_bytes":21956},{"path":"Evaluator/enums.py","sha256":"2ced9426d860e9ec410e376d9cf853d75e8e111e78dd0bee31e25bee295e4854","size_bytes":3371},{"path":"Evaluator/openai_compat_client.py","sha256":"6ac6211185d2c57d3b31022d31478e3253046fd6cb4de1903d8d0b209bad3a79","size_bytes":5174},{"path":"Evaluator/protocols.py","sha256":"239fe049b5fe4c9e9861aede487feac1f06a7642891378a1ccfba444072ad706","size_bytes":4560},{"path":"Evaluator/verified_vllm_chat.py","sha256":"f130e47d661aa77d68c104084fafe9e5df3af88684ef5a3139f4d53ff2e32465","size_bytes":5916},{"path":"Evaluator/vllm_client.py","sha256":"4e88d2055dc1b32cc22a1393cdf038ccd76d91d915a8be0774b0a8ec87bd4d1e","size_bytes":1995},{"path":"synaptic_tuner/__init__.py","sha256":"2462ff4ec439b77eb1ecfa901c1d8294628c853e9bbdf872de73dce9bfc0ae4d","size_bytes":134},{"path":"synaptic_tuner/_version.py","sha256":"49fa86b62849d537e7e131445f272650ed77760db5f218f2113155ab60caf644","size_bytes":96},{"path":"synaptic_tuner/api/__init__.py","sha256":"25c68ed273bf1e6b158021b98f8607276d980540a98b56a07801dccf9d6596a8","size_bytes":67},{"path":"synaptic_tuner/api/v1/__init__.py","sha256":"4bc30420b18b3932183dcd0485892fa22f4f8e474ca3bb9b5e08adf3ae0fde3d","size_bytes":16893},{"path":"synaptic_tuner/api/v1/_contract.py","sha256":"dcd6fc1ebdc75fbfd74ad4c6c96837b73209081258353557fedaaca76164673a","size_bytes":6702},{"path":"synaptic_tuner/api/v1/_timestamps.py","sha256":"2b2ad7183f62bf1d42c4b9144d0315fe90c0e601649fb37f7778d017c632935d","size_bytes":698},{"path":"synaptic_tuner/api/v1/planning.py","sha256":"e715b105a513fa56cfa510416e725471f8f3100cb34257be74bef3e40ca78460","size_bytes":9569},{"path":"synaptic_tuner/api/v1/providers.py","sha256":"eca5efd5a6dee453543ae338ffddf9451d34ce1743e4ab4bde024b47be571ec0","size_bytes":4940},{"path":"synaptic_tuner/api/v1/results.py","sha256":"65e530325c92c7afa18e3ca5f2ad9a11baf7d590221f08c33badfbf2d5f2c7b2","size_bytes":2236},{"path":"synaptic_tuner/api/v1/runs_facade.py","sha256":"bf6155726c99cfc5574385009e20f4f65b69a9ad4b96e33cf7065263d1d4c455","size_bytes":23572},{"path":"synaptic_tuner/api/v1/training_facade.py","sha256":"bf094d0e152f5861bf8a8ff51962e0700123d9b43ed91d914b9e78bbea6facd9","size_bytes":11118},{"path":"synaptic_tuner/api/v1/training_input.py","sha256":"209326325f8796ab7d038878e92def2c5521d7d98b7b9cc4dd1766d2e4f68c9d","size_bytes":38650},{"path":"tuner/__init__.py","sha256":"920cf8a0ca993a712fbacf734621e1fdf14fdd2a7203701db120dc41e4815e7b","size_bytes":2666},{"path":"tuner/cloud/__init__.py","sha256":"a9abb71aa9222c511524d3cc49b1cfe00f8938a6cb7c61792cf6135a6b82208b","size_bytes":3692},{"path":"tuner/cloud/runtime_layout.py","sha256":"2a4cd1f5fc63d95bb3f0b5bc4f44e616bb0308d9509aceb198943953746ff705","size_bytes":9435},{"path":"tuner/execution/__init__.py","sha256":"8193dfcd30c68a7d8ad02af0fb24f1a8120fce2a709decd132ab1acb33113d65","size_bytes":535},{"path":"tuner/execution/_effect_executor.py","sha256":"7aa3c523d971486417fe735c62a5cb3fe7e0698caa514d0da047293ad6b3e9cd","size_bytes":500},{"path":"tuner/execution/broker.py","sha256":"c504bd734b41ef9785fe5fa591966bb0b0bce3b43dcce0695b4ee0c142ca3d1e","size_bytes":6305},{"path":"tuner/execution/contracts.py","sha256":"82d90f742e4e33d8071e557b5a872e0158684d94db5debdfbb85411954e4e638","size_bytes":32907},{"path":"tuner/execution/coordinator_v1/__init__.py","sha256":"453718a1827cfb23e7ca0c908d3d256f62ca3e3fff8f75ed8a785f8336b17bbe","size_bytes":66},{"path":"tuner/execution/coordinator_v1/coordinator.py","sha256":"9bc8ecb60d472ad0b3c0d3192d8c73d88eb28dc43af55677fbdd4b3673b71ef9","size_bytes":47018},{"path":"tuner/execution/coordinator_v1/cursors.py","sha256":"5f7c899064dd26959aeed6b328b236b01f7ff549e2d29348bd45dc0480ede26b","size_bytes":9088},{"path":"tuner/execution/coordinator_v1/foundation.py","sha256":"4433c390075544df5516a5e213af4b4b697485ff7627f5a6dcace21a98029f94","size_bytes":12972},{"path":"tuner/execution/coordinator_v1/model.py","sha256":"1d2150d588f6b84f1b58913df398365a9491d6486a8a871bb2a22fa98cafc722","size_bytes":75837},{"path":"tuner/execution/coordinator_v1/ports.py","sha256":"7a028c32c3a3eab905bbc202248b4bf39411cd22e73fddacf5099b7d15dc3832","size_bytes":9343},{"path":"tuner/execution/coordinator_v1/state_machine.py","sha256":"e5e63db3a85fb9fc5b1d7bb5444691f88f308cfb0428842c0801b06d6f2fbb3a","size_bytes":50176},{"path":"tuner/execution/coordinator_v1/stores.py","sha256":"3a875f9e9d7d7d9899d5deba2f4d68cfada12bea499d5897592c9383ba2b16dc","size_bytes":39045},{"path":"tuner/execution/evidence.py","sha256":"a04cc6843a794e62a702608d28c8e82d0c57af8227b3cb9743cf56b9605442ca","size_bytes":4634},{"path":"tuner/execution/foundation_v2/__init__.py","sha256":"1651bf897879603a83e7824f60d369d52ac5d0308c96f988c24465f33112cd77","size_bytes":77},{"path":"tuner/execution/foundation_v2/authority.py","sha256":"a0eb5b3537a59c7b4d33e9b8617abd072376545d808f1989f562704dfa1ad024","size_bytes":9958},{"path":"tuner/execution/foundation_v2/canonical.py","sha256":"80620a6088d137bfacb210e000c406632bf569de66350628fb0e0aee612babfb","size_bytes":4047},{"path":"tuner/execution/foundation_v2/commands.py","sha256":"0fff7f05401094a60a390514622bbaa5649a8fb1b5f85f938e856192dac34e5c","size_bytes":8891},{"path":"tuner/execution/foundation_v2/executors.py","sha256":"27a428368c7aa4b9b502ae8028de03b48b817ea9d237a08dd112e7a6afc20454","size_bytes":5331},{"path":"tuner/execution/foundation_v2/identities.py","sha256":"720ae3938627f075c181eb5a125d118df87c9d6f7f24869107c74df9fbc44388","size_bytes":2794},{"path":"tuner/execution/foundation_v2/observations.py","sha256":"3642e4fd35478be5cb9a41df8f758ec4581d9f341f470bad1a4a2fbf8093636c","size_bytes":2566},{"path":"tuner/execution/foundation_v2/operations.py","sha256":"0f3c12d916a71a1fe20017b5ff7e99676c0acbf8bae7fac903d629c1aa3a9e6d","size_bytes":2259},{"path":"tuner/execution/foundation_v2/preparation.py","sha256":"09883f2455e521a7baa9b30fbb81518aa33ab2845392009d87cb54606ff80f1d","size_bytes":3575},{"path":"tuner/execution/foundation_v2/receipts.py","sha256":"c327476257236209ea1e29258fa55d90859bb262d27c132870d21399dfb5f626","size_bytes":15339},{"path":"tuner/execution/foundation_v2/references.py","sha256":"53d8e2070395a4c7f5c5a9361532f7a78de714003c760b55a23366394561b37f","size_bytes":3336},{"path":"tuner/execution/foundation_v2/repository.py","sha256":"d311b21fe8f2ed4b85d50ffa55fca370fe6452841d7a0189c5d56b841f69ef7f","size_bytes":57985},{"path":"tuner/execution/lifecycle.py","sha256":"4bc878038fc2384c0213a5f5c887aca8b15a67ed46b11e1acae1e2ecf0e97793","size_bytes":17660},{"path":"tuner/execution/operation.py","sha256":"9f86c585fc7ac3f7b2689a4701d598e32a382c3d1e0b87a7732ef35235568504","size_bytes":13702},{"path":"tuner/execution/providers/__init__.py","sha256":"8848731f5d1877c77ea8fcb66a82b85196aeafc06ba0e7463eca291ec1a5f41d","size_bytes":252},{"path":"tuner/execution/providers/contracts.py","sha256":"a21f22c8d373037d6426cd94dda48d7252438a08e115120188980dcaa34d6361","size_bytes":11506},{"path":"tuner/execution/providers/modal/__init__.py","sha256":"0f4e16bed9b4d05c59a5eef1d0ddc061720d5817a68a2bbe1073b20a19c27319","size_bytes":104},{"path":"tuner/execution/providers/modal/binding.py","sha256":"2d2acb7685798f487988b7690c1dbaf6ddc49e34ed8cd3600add438e17fc9ad5","size_bytes":1810},{"path":"tuner/execution/providers/modal/config.py","sha256":"bfa32ed26f3b8ece5e89e1a3524cb81e85ae3240e72ce93430ffb82dbb02f525","size_bytes":20788},{"path":"tuner/execution/providers/modal/contracts.py","sha256":"249e75a9ad13b93c65ac95cb100dc4fe24ed4c59674770832af3d5a9c4112dd2","size_bytes":6449},{"path":"tuner/execution/providers/modal/control.py","sha256":"19e05126196be5009c2cedac36a39008e82ca0bd17c9ec461f68f6ef26ece750","size_bytes":10359},{"path":"tuner/execution/providers/modal/coordinator_adapter.py","sha256":"619ba5e4e10f1a26ad4c154490da9328f9863581df02bf768e48e045ba46e413","size_bytes":13178},{"path":"tuner/execution/providers/modal/coordinator_binding.py","sha256":"e9267c37dce7273ffcad5f1db5332e205ba5282a9fbad4a647581e23c08b821e","size_bytes":4125},{"path":"tuner/execution/providers/modal/coordinator_bundle.py","sha256":"5d8a10d2c4a0dce83174eeca202c377730820ced6945be6f7717e3a0cfd91eb0","size_bytes":31316},{"path":"tuner/execution/providers/modal/coordinator_dispatch.py","sha256":"8e5bc475c6b7d15345e22e1f8cebdabcffab5a01ffefb7d4acbfb073be02b49f","size_bytes":10571},{"path":"tuner/execution/providers/modal/coordinator_launch.py","sha256":"5975bee2d73dd09e02008ede368ebc8f03325815e8bcfd4c093736214d5f0956","size_bytes":10982},{"path":"tuner/execution/providers/modal/coordinator_preflight.py","sha256":"f452e906d37fa9dba932e9df46f829447c95ec276191e34ec3e46831c89da875","size_bytes":13746},{"path":"tuner/execution/providers/modal/coordinator_reader.py","sha256":"92a1c21725587a88435d30b6b687f63711a12ad4c77345f0776521d6503103f4","size_bytes":28524},{"path":"tuner/execution/providers/modal/coordinator_staging.py","sha256":"de47b688e859ab96a612c01b1c7869d814e40b83ae81dcd7f2b1132e18c794f0","size_bytes":20880},{"path":"tuner/execution/providers/modal/coordinator_submit_preparation.py","sha256":"28814779c7f086016ae3ff617e0f1d4cbd8b52f518d3d889a905ea03d70456ec","size_bytes":5213},{"path":"tuner/execution/providers/modal/coordinator_wire.py","sha256":"07d46b92b33e30cb45bb6b1139ce4f80afdb09e1a17a3f3c2526ddcfae6983c9","size_bytes":12998},{"path":"tuner/execution/providers/modal/deployment_identity.py","sha256":"1b7007e5a9c11abdfe0ad4f106f742faa0ee556921b4017d3b48cb772bd0da4f","size_bytes":1196},{"path":"tuner/execution/providers/modal/deployment_v1.py","sha256":"579ace8adbddfe30e09d5a39df9926a54b111a139c704fc7efc7cebf533acf94","size_bytes":3628},{"path":"tuner/execution/providers/modal/facade.py","sha256":"55b89b3d0faff6238eeead068d6963d26f27749d725d2f2b8ae32449b348e32d","size_bytes":17704},{"path":"tuner/execution/providers/modal/inference_artifacts.py","sha256":"5816f0c5662de8edad752554ba149d9527ced40ebdceeccc993c136ab099bff3","size_bytes":14973},{"path":"tuner/execution/providers/modal/inference_binding.py","sha256":"f2e8c4f1d8fc1f5e70702c88cbfff044a1f4f569e06b9a31c33a0cb9aaaf5894","size_bytes":23363},{"path":"tuner/execution/providers/modal/inference_bootstrap.py","sha256":"db2f09c1dfb2aee667b46fbb6566be8b2922d8e762896e5577d5c5078ad7d122","size_bytes":12546},{"path":"tuner/execution/providers/modal/inference_channel.py","sha256":"23492b4c7f48d86d23fb21ffd3902ac3f48bae381c30ea4759c803577911592e","size_bytes":13438},{"path":"tuner/execution/providers/modal/inference_commands.py","sha256":"1fa2dd667cb3b33e012d37b0688bfe08fc7a7cec52230a202f1f9ddda70110c5","size_bytes":4977},{"path":"tuner/execution/providers/modal/inference_entrypoint.py","sha256":"6ec8bdcbbf6d87fb5fc02a2c42d2c0f344ea965ccc4c0d94a630055c6e779991","size_bytes":10873},{"path":"tuner/execution/providers/modal/inference_model.py","sha256":"c08e1a35bb71a11c3466aedfbd8d3de87cf32d6cbd85820c3dcde2fefd14fd65","size_bytes":5935},{"path":"tuner/execution/providers/modal/inference_preparation.py","sha256":"43a1c883a8e5b2f25364e3a20f59d92a852af15fda0c9d506c213c0bf185fc32","size_bytes":49436},{"path":"tuner/execution/providers/modal/inference_runtime.py","sha256":"f8ba0d5744f90ca95e1fea3e5faf293e153a91047ffb09596f0305e1339d56ee","size_bytes":19283},{"path":"tuner/execution/providers/modal/inference_wire.py","sha256":"0492c10c6a384596bba04b3b409dc34f1f1a4afa88b7321d0d1a6fac5e29a1fb","size_bytes":20150},{"path":"tuner/execution/providers/modal/inference_worker.py","sha256":"cfb27e938c60757ef08c80b918fb522012c84918e7a0cd41a63de2cb2486cb87","size_bytes":15657},{"path":"tuner/execution/providers/modal/inference_workload.py","sha256":"4eaf399485e76829a98f57907c012658ce41d02745978827f21262ce2be94ff8","size_bytes":20421},{"path":"tuner/execution/providers/modal/manifest.py","sha256":"fc2ab4dcc46d136a681bf124d7a9fc3e5deedeeb3641d798fd207cdb62bb506f","size_bytes":8579},{"path":"tuner/execution/providers/modal/model_snapshot.py","sha256":"9d4762b821b2944a8a62932d0aec105c2f6920d64bf4ceed6907e5458799e337","size_bytes":14166},{"path":"tuner/execution/providers/modal/mounted_io.py","sha256":"e49da2e749d3be1ca19fdc7f581b29fb6d1e748a86fffcf0fde2be5cdc1a0d5b","size_bytes":13076},{"path":"tuner/execution/providers/modal/prepared_input.py","sha256":"c16cef5cfcc73ab9a7826977b26e18912ac46409d91b9eae165893ed265896ac","size_bytes":8597},{"path":"tuner/execution/providers/modal/resolution.py","sha256":"5c7268aa644103ae0712b03ed55918527d6e2ed6f4120ef169866b0ffd1cbf6f","size_bytes":30179},{"path":"tuner/execution/providers/modal/runtime.py","sha256":"ada13733021a6dab19331da80076df630055abc5bddc192a34a5cda65ac718c4","size_bytes":14810},{"path":"tuner/execution/providers/modal/worker_ports.py","sha256":"adf334368dbb75e40177078cd32aedc445cd9dde8b5ece2401319d472dbccdb0","size_bytes":2928},{"path":"tuner/execution/registry.py","sha256":"0cf424ff4b6bd628bae86b347f00a10acce8793cc41afc2ff1f3abbb738aec40","size_bytes":1515},{"path":"tuner/execution/service.py","sha256":"a85e31e8a54479764463633a9c721b9af3afe38710f85efa35b9a5744ad33e4c","size_bytes":4514},{"path":"tuner/inference/__init__.py","sha256":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","size_bytes":0},{"path":"tuner/inference/chat_session.py","sha256":"b95a829f834406a0a0c152f9af713225fe205a9225ad7b0f959b1ba801dbcac2","size_bytes":22355},{"path":"tuner/inference/owned_process.py","sha256":"f4cbb4e1c2305615b2c127e1103f60b1e17e955fe20a2a847b1d88b7ba8c4c98","size_bytes":8832},{"path":"tuner/inference/retrieved_model.py","sha256":"4a8cf2b2cbe4f03bf7c4d240c5e59a6ec6d20826c686b3a06c0bc1642fa234d8","size_bytes":31703},{"path":"tuner/inference/run_chat.py","sha256":"9263d325224bee4b9a0e7c16045e30766d9a2e2d2b550f0508040161687d3cfc","size_bytes":6228},{"path":"tuner/inference/serving_target.py","sha256":"50ec8054d1d6d08724d7a29183ca54a04d9c915d2c04274bdac6834764bdb800","size_bytes":12227},{"path":"tuner/inference/vllm_runtime.py","sha256":"fafbcfdeee78143a2760e5fbf29621897e9be014814eefb69f1167b188c296c1","size_bytes":26019},{"path":"tuner/project/__init__.py","sha256":"a858562f45452031f8a0e8ba4c9799f9784e09360a0258165dc3c4f89a46ca23","size_bytes":1513},{"path":"tuner/project/config_layers.py","sha256":"0e06ddfb8b92faa106d052990df34f199c06c298c3a3a2366cab218f2c1731f1","size_bytes":6714},{"path":"tuner/project/context.py","sha256":"147d08d5d6fcb3cf258ab347f070424e65e5c0c28e47ad1fd062e13cf56a8dd3","size_bytes":6518},{"path":"tuner/project/errors.py","sha256":"6f1f94cd0b4e068a0cdee82259de0aee09353345e93caeb05272097a5e717444","size_bytes":1511},{"path":"tuner/project/execution_source.py","sha256":"dc196dff10029c9a77b6d94fbfbcca45b39da4840bff40009b7152fd39d9622b","size_bytes":30595},{"path":"tuner/project/git_verification.py","sha256":"0cfe00560c704eddbe688a0c7156c12f9acd2f321046017f50eb5d9e3bb61710","size_bytes":9767},{"path":"tuner/project/manifest.py","sha256":"5506cf2ffb261988c0bbc5ffab1e466236bd10f503df5e805453f5ddf7d34be0","size_bytes":9498},{"path":"tuner/project/path_refs.py","sha256":"35e47de6ed1c6096ba45e7ea64aedcc0f79aeb9425acb5041b14afb9d258c955","size_bytes":6287},{"path":"tuner/project/secrets.py","sha256":"6f267060b0f24ab1517c4d4499fe54a993a77c2c78830c2d79415da871893818","size_bytes":4256},{"path":"tuner/project/source_bundle.py","sha256":"16368658db727680f74326603e2d28f3ae9b5e481f7626c7be1a8280b588ecec","size_bytes":28339},{"path":"tuner/runtime/__init__.py","sha256":"246429ce58a07dabeb05bf17967d3a7862014d114ee995a7419adcb27227286c","size_bytes":3566},{"path":"tuner/runtime/artifacts.py","sha256":"c9eaefd6f6805af11d997256e14392c0c42173b852d94bfab90bcb20a1ae2cd2","size_bytes":6913},{"path":"tuner/runtime/dispatch.py","sha256":"4c41faf87d7e620bdc281490bf3725550b83dacca8b15ea47bab253f669cdd8e","size_bytes":35463},{"path":"tuner/runtime/offline_sft_worker.py","sha256":"248f258254bb9c7021eed2ee0ff32d9633a021e656c10efb15825e7f6aaa9366","size_bytes":32592},{"path":"tuner/runtime/releases.py","sha256":"c0aa7ba37e8d35287fdd480cf0c76053a554e546c40c8bf36f5cf05d1a96d22b","size_bytes":52535},{"path":"tuner/runtime/verification.py","sha256":"4c2314b20cbf7937a3f24aa90676000b9e21f0ab6701b2c1225a53e5596bf4a0","size_bytes":53856},{"path":"tuner/training/__init__.py","sha256":"23ca59d6203276c6160e0bb57dbd5c43cf385c6aec60b4e3e08b93d1d501cb97","size_bytes":2995},{"path":"tuner/training/contracts.py","sha256":"91c4d66d9810cc0471981c3d4ef4b54feaed5b92eb41a468f31201f8111303b5","size_bytes":21504},{"path":"tuner/training/coordinator_material.py","sha256":"c563aa3b1fbd940ddd3b8e56e4f33de70675cb831e938175f40debc0645fd9c7","size_bytes":12950},{"path":"tuner/training/grpo_compilation.py","sha256":"c6268486375bbc16f1888bb43d07b93a8de280d000a774914d59d7fd05ca5cc0","size_bytes":8998},{"path":"tuner/training/methods/__init__.py","sha256":"99db689246273707ae3df5087859ebb510e84b664f9f173c0d5827abc3e6a3ea","size_bytes":361},{"path":"tuner/training/methods/sft.py","sha256":"b501f9c214dec4b0b1550543281c25aba8dbb102604df253c641c85f7cae587b","size_bytes":9997},{"path":"tuner/training/packaged_compilation.py","sha256":"477caede6902fc587af680889b93fd9e0e42493d1a6653f3cd7413a9c7cadde8","size_bytes":9413},{"path":"tuner/training/packaged_grpo_compilation.py","sha256":"d186a05ffb8cc756e782b2bb9a3f66e925779104b70045bd56a5592846fec524","size_bytes":8722},{"path":"tuner/training/post_training.py","sha256":"ba8c2ea3fe49a647d502c6a874a44658cc2e575d4cd5729966102b0ebb973a88","size_bytes":7971},{"path":"tuner/training/recipes.py","sha256":"bb844ed4883f00ccea9936eebad50c466e6e4bc148e88aa680d93121edf67dcc","size_bytes":6300}],"payload_bytes":1653052,"schema_version":"synaptic-modal-inference-worker-closure/v1"} +{"closure_digest":"11b8b9df877b6dbb3a50268f7782e8ed0f6b291e85a5b4981ff0a2f8edf47175","entrypoint":"tuner/execution/providers/modal/inference_bootstrap.py","member_count":124,"members":[{"path":"Evaluator/__init__.py","sha256":"d1b5aea793a95c47618d4b2469a8bae0b14fd11f81501c1d71546469a39a5cbd","size_bytes":3061},{"path":"Evaluator/base_client.py","sha256":"ac7e77909bdf161a4fd9b1ad2e1e2cfe085ec31f36baa6b19f8976f7b2358cbd","size_bytes":19362},{"path":"Evaluator/config.py","sha256":"94fbe8e198d065141ff9ad002b0a6e26e56d865fc1e3a3fb23479a8e69d09303","size_bytes":21956},{"path":"Evaluator/enums.py","sha256":"2ced9426d860e9ec410e376d9cf853d75e8e111e78dd0bee31e25bee295e4854","size_bytes":3371},{"path":"Evaluator/openai_compat_client.py","sha256":"6ac6211185d2c57d3b31022d31478e3253046fd6cb4de1903d8d0b209bad3a79","size_bytes":5174},{"path":"Evaluator/protocols.py","sha256":"239fe049b5fe4c9e9861aede487feac1f06a7642891378a1ccfba444072ad706","size_bytes":4560},{"path":"Evaluator/verified_vllm_chat.py","sha256":"f130e47d661aa77d68c104084fafe9e5df3af88684ef5a3139f4d53ff2e32465","size_bytes":5916},{"path":"Evaluator/vllm_client.py","sha256":"4e88d2055dc1b32cc22a1393cdf038ccd76d91d915a8be0774b0a8ec87bd4d1e","size_bytes":1995},{"path":"synaptic_tuner/__init__.py","sha256":"2462ff4ec439b77eb1ecfa901c1d8294628c853e9bbdf872de73dce9bfc0ae4d","size_bytes":134},{"path":"synaptic_tuner/_version.py","sha256":"49fa86b62849d537e7e131445f272650ed77760db5f218f2113155ab60caf644","size_bytes":96},{"path":"synaptic_tuner/api/__init__.py","sha256":"25c68ed273bf1e6b158021b98f8607276d980540a98b56a07801dccf9d6596a8","size_bytes":67},{"path":"synaptic_tuner/api/v1/__init__.py","sha256":"4bc30420b18b3932183dcd0485892fa22f4f8e474ca3bb9b5e08adf3ae0fde3d","size_bytes":16893},{"path":"synaptic_tuner/api/v1/_contract.py","sha256":"dcd6fc1ebdc75fbfd74ad4c6c96837b73209081258353557fedaaca76164673a","size_bytes":6702},{"path":"synaptic_tuner/api/v1/_timestamps.py","sha256":"2b2ad7183f62bf1d42c4b9144d0315fe90c0e601649fb37f7778d017c632935d","size_bytes":698},{"path":"synaptic_tuner/api/v1/planning.py","sha256":"e715b105a513fa56cfa510416e725471f8f3100cb34257be74bef3e40ca78460","size_bytes":9569},{"path":"synaptic_tuner/api/v1/providers.py","sha256":"eca5efd5a6dee453543ae338ffddf9451d34ce1743e4ab4bde024b47be571ec0","size_bytes":4940},{"path":"synaptic_tuner/api/v1/results.py","sha256":"65e530325c92c7afa18e3ca5f2ad9a11baf7d590221f08c33badfbf2d5f2c7b2","size_bytes":2236},{"path":"synaptic_tuner/api/v1/runs_facade.py","sha256":"bf6155726c99cfc5574385009e20f4f65b69a9ad4b96e33cf7065263d1d4c455","size_bytes":23572},{"path":"synaptic_tuner/api/v1/training_facade.py","sha256":"bf094d0e152f5861bf8a8ff51962e0700123d9b43ed91d914b9e78bbea6facd9","size_bytes":11118},{"path":"synaptic_tuner/api/v1/training_input.py","sha256":"209326325f8796ab7d038878e92def2c5521d7d98b7b9cc4dd1766d2e4f68c9d","size_bytes":38650},{"path":"tuner/__init__.py","sha256":"920cf8a0ca993a712fbacf734621e1fdf14fdd2a7203701db120dc41e4815e7b","size_bytes":2666},{"path":"tuner/cloud/__init__.py","sha256":"a9abb71aa9222c511524d3cc49b1cfe00f8938a6cb7c61792cf6135a6b82208b","size_bytes":3692},{"path":"tuner/cloud/runtime_layout.py","sha256":"2a4cd1f5fc63d95bb3f0b5bc4f44e616bb0308d9509aceb198943953746ff705","size_bytes":9435},{"path":"tuner/execution/__init__.py","sha256":"8193dfcd30c68a7d8ad02af0fb24f1a8120fce2a709decd132ab1acb33113d65","size_bytes":535},{"path":"tuner/execution/_effect_executor.py","sha256":"7aa3c523d971486417fe735c62a5cb3fe7e0698caa514d0da047293ad6b3e9cd","size_bytes":500},{"path":"tuner/execution/broker.py","sha256":"c504bd734b41ef9785fe5fa591966bb0b0bce3b43dcce0695b4ee0c142ca3d1e","size_bytes":6305},{"path":"tuner/execution/contracts.py","sha256":"82d90f742e4e33d8071e557b5a872e0158684d94db5debdfbb85411954e4e638","size_bytes":32907},{"path":"tuner/execution/coordinator_v1/__init__.py","sha256":"453718a1827cfb23e7ca0c908d3d256f62ca3e3fff8f75ed8a785f8336b17bbe","size_bytes":66},{"path":"tuner/execution/coordinator_v1/coordinator.py","sha256":"9bc8ecb60d472ad0b3c0d3192d8c73d88eb28dc43af55677fbdd4b3673b71ef9","size_bytes":47018},{"path":"tuner/execution/coordinator_v1/cursors.py","sha256":"5f7c899064dd26959aeed6b328b236b01f7ff549e2d29348bd45dc0480ede26b","size_bytes":9088},{"path":"tuner/execution/coordinator_v1/foundation.py","sha256":"4433c390075544df5516a5e213af4b4b697485ff7627f5a6dcace21a98029f94","size_bytes":12972},{"path":"tuner/execution/coordinator_v1/model.py","sha256":"1d2150d588f6b84f1b58913df398365a9491d6486a8a871bb2a22fa98cafc722","size_bytes":75837},{"path":"tuner/execution/coordinator_v1/ports.py","sha256":"7a028c32c3a3eab905bbc202248b4bf39411cd22e73fddacf5099b7d15dc3832","size_bytes":9343},{"path":"tuner/execution/coordinator_v1/state_machine.py","sha256":"e5e63db3a85fb9fc5b1d7bb5444691f88f308cfb0428842c0801b06d6f2fbb3a","size_bytes":50176},{"path":"tuner/execution/coordinator_v1/stores.py","sha256":"3a875f9e9d7d7d9899d5deba2f4d68cfada12bea499d5897592c9383ba2b16dc","size_bytes":39045},{"path":"tuner/execution/evidence.py","sha256":"a04cc6843a794e62a702608d28c8e82d0c57af8227b3cb9743cf56b9605442ca","size_bytes":4634},{"path":"tuner/execution/foundation_v2/__init__.py","sha256":"1651bf897879603a83e7824f60d369d52ac5d0308c96f988c24465f33112cd77","size_bytes":77},{"path":"tuner/execution/foundation_v2/authority.py","sha256":"a0eb5b3537a59c7b4d33e9b8617abd072376545d808f1989f562704dfa1ad024","size_bytes":9958},{"path":"tuner/execution/foundation_v2/canonical.py","sha256":"80620a6088d137bfacb210e000c406632bf569de66350628fb0e0aee612babfb","size_bytes":4047},{"path":"tuner/execution/foundation_v2/commands.py","sha256":"0fff7f05401094a60a390514622bbaa5649a8fb1b5f85f938e856192dac34e5c","size_bytes":8891},{"path":"tuner/execution/foundation_v2/executors.py","sha256":"27a428368c7aa4b9b502ae8028de03b48b817ea9d237a08dd112e7a6afc20454","size_bytes":5331},{"path":"tuner/execution/foundation_v2/identities.py","sha256":"720ae3938627f075c181eb5a125d118df87c9d6f7f24869107c74df9fbc44388","size_bytes":2794},{"path":"tuner/execution/foundation_v2/observations.py","sha256":"3642e4fd35478be5cb9a41df8f758ec4581d9f341f470bad1a4a2fbf8093636c","size_bytes":2566},{"path":"tuner/execution/foundation_v2/operations.py","sha256":"0f3c12d916a71a1fe20017b5ff7e99676c0acbf8bae7fac903d629c1aa3a9e6d","size_bytes":2259},{"path":"tuner/execution/foundation_v2/preparation.py","sha256":"09883f2455e521a7baa9b30fbb81518aa33ab2845392009d87cb54606ff80f1d","size_bytes":3575},{"path":"tuner/execution/foundation_v2/receipts.py","sha256":"c327476257236209ea1e29258fa55d90859bb262d27c132870d21399dfb5f626","size_bytes":15339},{"path":"tuner/execution/foundation_v2/references.py","sha256":"53d8e2070395a4c7f5c5a9361532f7a78de714003c760b55a23366394561b37f","size_bytes":3336},{"path":"tuner/execution/foundation_v2/repository.py","sha256":"d311b21fe8f2ed4b85d50ffa55fca370fe6452841d7a0189c5d56b841f69ef7f","size_bytes":57985},{"path":"tuner/execution/lifecycle.py","sha256":"4bc878038fc2384c0213a5f5c887aca8b15a67ed46b11e1acae1e2ecf0e97793","size_bytes":17660},{"path":"tuner/execution/operation.py","sha256":"9f86c585fc7ac3f7b2689a4701d598e32a382c3d1e0b87a7732ef35235568504","size_bytes":13702},{"path":"tuner/execution/providers/__init__.py","sha256":"8848731f5d1877c77ea8fcb66a82b85196aeafc06ba0e7463eca291ec1a5f41d","size_bytes":252},{"path":"tuner/execution/providers/contracts.py","sha256":"a21f22c8d373037d6426cd94dda48d7252438a08e115120188980dcaa34d6361","size_bytes":11506},{"path":"tuner/execution/providers/modal/__init__.py","sha256":"0f4e16bed9b4d05c59a5eef1d0ddc061720d5817a68a2bbe1073b20a19c27319","size_bytes":104},{"path":"tuner/execution/providers/modal/binding.py","sha256":"2d2acb7685798f487988b7690c1dbaf6ddc49e34ed8cd3600add438e17fc9ad5","size_bytes":1810},{"path":"tuner/execution/providers/modal/config.py","sha256":"bfa32ed26f3b8ece5e89e1a3524cb81e85ae3240e72ce93430ffb82dbb02f525","size_bytes":20788},{"path":"tuner/execution/providers/modal/contracts.py","sha256":"249e75a9ad13b93c65ac95cb100dc4fe24ed4c59674770832af3d5a9c4112dd2","size_bytes":6449},{"path":"tuner/execution/providers/modal/control.py","sha256":"19e05126196be5009c2cedac36a39008e82ca0bd17c9ec461f68f6ef26ece750","size_bytes":10359},{"path":"tuner/execution/providers/modal/coordinator_adapter.py","sha256":"619ba5e4e10f1a26ad4c154490da9328f9863581df02bf768e48e045ba46e413","size_bytes":13178},{"path":"tuner/execution/providers/modal/coordinator_binding.py","sha256":"e9267c37dce7273ffcad5f1db5332e205ba5282a9fbad4a647581e23c08b821e","size_bytes":4125},{"path":"tuner/execution/providers/modal/coordinator_bundle.py","sha256":"5d8a10d2c4a0dce83174eeca202c377730820ced6945be6f7717e3a0cfd91eb0","size_bytes":31316},{"path":"tuner/execution/providers/modal/coordinator_dispatch.py","sha256":"8e5bc475c6b7d15345e22e1f8cebdabcffab5a01ffefb7d4acbfb073be02b49f","size_bytes":10571},{"path":"tuner/execution/providers/modal/coordinator_launch.py","sha256":"5975bee2d73dd09e02008ede368ebc8f03325815e8bcfd4c093736214d5f0956","size_bytes":10982},{"path":"tuner/execution/providers/modal/coordinator_preflight.py","sha256":"f452e906d37fa9dba932e9df46f829447c95ec276191e34ec3e46831c89da875","size_bytes":13746},{"path":"tuner/execution/providers/modal/coordinator_reader.py","sha256":"92a1c21725587a88435d30b6b687f63711a12ad4c77345f0776521d6503103f4","size_bytes":28524},{"path":"tuner/execution/providers/modal/coordinator_staging.py","sha256":"de47b688e859ab96a612c01b1c7869d814e40b83ae81dcd7f2b1132e18c794f0","size_bytes":20880},{"path":"tuner/execution/providers/modal/coordinator_submit_preparation.py","sha256":"28814779c7f086016ae3ff617e0f1d4cbd8b52f518d3d889a905ea03d70456ec","size_bytes":5213},{"path":"tuner/execution/providers/modal/coordinator_wire.py","sha256":"07d46b92b33e30cb45bb6b1139ce4f80afdb09e1a17a3f3c2526ddcfae6983c9","size_bytes":12998},{"path":"tuner/execution/providers/modal/deployment_identity.py","sha256":"1b7007e5a9c11abdfe0ad4f106f742faa0ee556921b4017d3b48cb772bd0da4f","size_bytes":1196},{"path":"tuner/execution/providers/modal/deployment_v1.py","sha256":"579ace8adbddfe30e09d5a39df9926a54b111a139c704fc7efc7cebf533acf94","size_bytes":3628},{"path":"tuner/execution/providers/modal/facade.py","sha256":"55b89b3d0faff6238eeead068d6963d26f27749d725d2f2b8ae32449b348e32d","size_bytes":17704},{"path":"tuner/execution/providers/modal/inference_artifacts.py","sha256":"5816f0c5662de8edad752554ba149d9527ced40ebdceeccc993c136ab099bff3","size_bytes":14973},{"path":"tuner/execution/providers/modal/inference_binding.py","sha256":"f2e8c4f1d8fc1f5e70702c88cbfff044a1f4f569e06b9a31c33a0cb9aaaf5894","size_bytes":23363},{"path":"tuner/execution/providers/modal/inference_bootstrap.py","sha256":"db2f09c1dfb2aee667b46fbb6566be8b2922d8e762896e5577d5c5078ad7d122","size_bytes":12546},{"path":"tuner/execution/providers/modal/inference_channel.py","sha256":"23492b4c7f48d86d23fb21ffd3902ac3f48bae381c30ea4759c803577911592e","size_bytes":13438},{"path":"tuner/execution/providers/modal/inference_commands.py","sha256":"1fa2dd667cb3b33e012d37b0688bfe08fc7a7cec52230a202f1f9ddda70110c5","size_bytes":4977},{"path":"tuner/execution/providers/modal/inference_entrypoint.py","sha256":"6ec8bdcbbf6d87fb5fc02a2c42d2c0f344ea965ccc4c0d94a630055c6e779991","size_bytes":10873},{"path":"tuner/execution/providers/modal/inference_model.py","sha256":"c08e1a35bb71a11c3466aedfbd8d3de87cf32d6cbd85820c3dcde2fefd14fd65","size_bytes":5935},{"path":"tuner/execution/providers/modal/inference_preparation.py","sha256":"43a1c883a8e5b2f25364e3a20f59d92a852af15fda0c9d506c213c0bf185fc32","size_bytes":49436},{"path":"tuner/execution/providers/modal/inference_runtime.py","sha256":"f8ba0d5744f90ca95e1fea3e5faf293e153a91047ffb09596f0305e1339d56ee","size_bytes":19283},{"path":"tuner/execution/providers/modal/inference_wire.py","sha256":"0492c10c6a384596bba04b3b409dc34f1f1a4afa88b7321d0d1a6fac5e29a1fb","size_bytes":20150},{"path":"tuner/execution/providers/modal/inference_worker.py","sha256":"cfb27e938c60757ef08c80b918fb522012c84918e7a0cd41a63de2cb2486cb87","size_bytes":15657},{"path":"tuner/execution/providers/modal/inference_workload.py","sha256":"4eaf399485e76829a98f57907c012658ce41d02745978827f21262ce2be94ff8","size_bytes":20421},{"path":"tuner/execution/providers/modal/manifest.py","sha256":"fc2ab4dcc46d136a681bf124d7a9fc3e5deedeeb3641d798fd207cdb62bb506f","size_bytes":8579},{"path":"tuner/execution/providers/modal/model_snapshot.py","sha256":"9d4762b821b2944a8a62932d0aec105c2f6920d64bf4ceed6907e5458799e337","size_bytes":14166},{"path":"tuner/execution/providers/modal/mounted_io.py","sha256":"e49da2e749d3be1ca19fdc7f581b29fb6d1e748a86fffcf0fde2be5cdc1a0d5b","size_bytes":13076},{"path":"tuner/execution/providers/modal/prepared_input.py","sha256":"c16cef5cfcc73ab9a7826977b26e18912ac46409d91b9eae165893ed265896ac","size_bytes":8597},{"path":"tuner/execution/providers/modal/resolution.py","sha256":"5c7268aa644103ae0712b03ed55918527d6e2ed6f4120ef169866b0ffd1cbf6f","size_bytes":30179},{"path":"tuner/execution/providers/modal/runtime.py","sha256":"ada13733021a6dab19331da80076df630055abc5bddc192a34a5cda65ac718c4","size_bytes":14810},{"path":"tuner/execution/providers/modal/worker_ports.py","sha256":"adf334368dbb75e40177078cd32aedc445cd9dde8b5ece2401319d472dbccdb0","size_bytes":2928},{"path":"tuner/execution/registry.py","sha256":"0cf424ff4b6bd628bae86b347f00a10acce8793cc41afc2ff1f3abbb738aec40","size_bytes":1515},{"path":"tuner/execution/service.py","sha256":"a85e31e8a54479764463633a9c721b9af3afe38710f85efa35b9a5744ad33e4c","size_bytes":4514},{"path":"tuner/inference/__init__.py","sha256":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","size_bytes":0},{"path":"tuner/inference/chat_session.py","sha256":"b95a829f834406a0a0c152f9af713225fe205a9225ad7b0f959b1ba801dbcac2","size_bytes":22355},{"path":"tuner/inference/owned_process.py","sha256":"f4cbb4e1c2305615b2c127e1103f60b1e17e955fe20a2a847b1d88b7ba8c4c98","size_bytes":8832},{"path":"tuner/inference/retrieved_model.py","sha256":"4a8cf2b2cbe4f03bf7c4d240c5e59a6ec6d20826c686b3a06c0bc1642fa234d8","size_bytes":31703},{"path":"tuner/inference/run_chat.py","sha256":"9263d325224bee4b9a0e7c16045e30766d9a2e2d2b550f0508040161687d3cfc","size_bytes":6228},{"path":"tuner/inference/serving_target.py","sha256":"50ec8054d1d6d08724d7a29183ca54a04d9c915d2c04274bdac6834764bdb800","size_bytes":12227},{"path":"tuner/inference/vllm_runtime.py","sha256":"fafbcfdeee78143a2760e5fbf29621897e9be014814eefb69f1167b188c296c1","size_bytes":26019},{"path":"tuner/project/__init__.py","sha256":"a858562f45452031f8a0e8ba4c9799f9784e09360a0258165dc3c4f89a46ca23","size_bytes":1513},{"path":"tuner/project/config_layers.py","sha256":"0e06ddfb8b92faa106d052990df34f199c06c298c3a3a2366cab218f2c1731f1","size_bytes":6714},{"path":"tuner/project/context.py","sha256":"147d08d5d6fcb3cf258ab347f070424e65e5c0c28e47ad1fd062e13cf56a8dd3","size_bytes":6518},{"path":"tuner/project/errors.py","sha256":"6f1f94cd0b4e068a0cdee82259de0aee09353345e93caeb05272097a5e717444","size_bytes":1511},{"path":"tuner/project/execution_source.py","sha256":"dc196dff10029c9a77b6d94fbfbcca45b39da4840bff40009b7152fd39d9622b","size_bytes":30595},{"path":"tuner/project/git_verification.py","sha256":"0cfe00560c704eddbe688a0c7156c12f9acd2f321046017f50eb5d9e3bb61710","size_bytes":9767},{"path":"tuner/project/manifest.py","sha256":"5506cf2ffb261988c0bbc5ffab1e466236bd10f503df5e805453f5ddf7d34be0","size_bytes":9498},{"path":"tuner/project/path_refs.py","sha256":"35e47de6ed1c6096ba45e7ea64aedcc0f79aeb9425acb5041b14afb9d258c955","size_bytes":6287},{"path":"tuner/project/secrets.py","sha256":"6f267060b0f24ab1517c4d4499fe54a993a77c2c78830c2d79415da871893818","size_bytes":4256},{"path":"tuner/project/source_bundle.py","sha256":"16368658db727680f74326603e2d28f3ae9b5e481f7626c7be1a8280b588ecec","size_bytes":28339},{"path":"tuner/runtime/__init__.py","sha256":"246429ce58a07dabeb05bf17967d3a7862014d114ee995a7419adcb27227286c","size_bytes":3566},{"path":"tuner/runtime/artifacts.py","sha256":"c9eaefd6f6805af11d997256e14392c0c42173b852d94bfab90bcb20a1ae2cd2","size_bytes":6913},{"path":"tuner/runtime/dispatch.py","sha256":"4c41faf87d7e620bdc281490bf3725550b83dacca8b15ea47bab253f669cdd8e","size_bytes":35463},{"path":"tuner/runtime/offline_sft_worker.py","sha256":"2304eeb00667954510c34cc4aacaa818532a301fbeabf04c2fb165d6cd1d70b5","size_bytes":36343},{"path":"tuner/runtime/releases.py","sha256":"c0aa7ba37e8d35287fdd480cf0c76053a554e546c40c8bf36f5cf05d1a96d22b","size_bytes":52535},{"path":"tuner/runtime/verification.py","sha256":"4c2314b20cbf7937a3f24aa90676000b9e21f0ab6701b2c1225a53e5596bf4a0","size_bytes":53856},{"path":"tuner/training/__init__.py","sha256":"23ca59d6203276c6160e0bb57dbd5c43cf385c6aec60b4e3e08b93d1d501cb97","size_bytes":2995},{"path":"tuner/training/contracts.py","sha256":"91c4d66d9810cc0471981c3d4ef4b54feaed5b92eb41a468f31201f8111303b5","size_bytes":21504},{"path":"tuner/training/coordinator_material.py","sha256":"c563aa3b1fbd940ddd3b8e56e4f33de70675cb831e938175f40debc0645fd9c7","size_bytes":12950},{"path":"tuner/training/grpo_compilation.py","sha256":"c6268486375bbc16f1888bb43d07b93a8de280d000a774914d59d7fd05ca5cc0","size_bytes":8998},{"path":"tuner/training/methods/__init__.py","sha256":"99db689246273707ae3df5087859ebb510e84b664f9f173c0d5827abc3e6a3ea","size_bytes":361},{"path":"tuner/training/methods/sft.py","sha256":"b501f9c214dec4b0b1550543281c25aba8dbb102604df253c641c85f7cae587b","size_bytes":9997},{"path":"tuner/training/packaged_compilation.py","sha256":"477caede6902fc587af680889b93fd9e0e42493d1a6653f3cd7413a9c7cadde8","size_bytes":9413},{"path":"tuner/training/packaged_grpo_compilation.py","sha256":"d186a05ffb8cc756e782b2bb9a3f66e925779104b70045bd56a5592846fec524","size_bytes":8722},{"path":"tuner/training/post_training.py","sha256":"ba8c2ea3fe49a647d502c6a874a44658cc2e575d4cd5729966102b0ebb973a88","size_bytes":7971},{"path":"tuner/training/recipes.py","sha256":"bb844ed4883f00ccea9936eebad50c466e6e4bc148e88aa680d93121edf67dcc","size_bytes":6300}],"payload_bytes":1656803,"schema_version":"synaptic-modal-inference-worker-closure/v1"} diff --git a/tuner/execution/providers/modal/modal-runtime-v1.lock.json b/tuner/execution/providers/modal/modal-runtime-v1.lock.json index cdd5c34b..e27d0cca 100644 --- a/tuner/execution/providers/modal/modal-runtime-v1.lock.json +++ b/tuner/execution/providers/modal/modal-runtime-v1.lock.json @@ -318,11 +318,11 @@ }, "bootstrap__tuner__runtime__manifests__offline-sft-worker-v1_json": { "path": "tuner/runtime/manifests/offline-sft-worker-v1.json", - "sha256": "f9663e95c0d1e8c86ea34dca1e1876014fab4636569c95ef3c10982bccd17915" + "sha256": "3842689403abc5b42e28d58545b88616b81cd8fc738ee5557e97159df65d00ef" }, "bootstrap__tuner__runtime__offline_sft_worker_py": { "path": "tuner/runtime/offline_sft_worker.py", - "sha256": "248f258254bb9c7021eed2ee0ff32d9633a021e656c10efb15825e7f6aaa9366" + "sha256": "2304eeb00667954510c34cc4aacaa818532a301fbeabf04c2fb165d6cd1d70b5" }, "bootstrap__tuner__training____init___py": { "path": "tuner/training/__init__.py", diff --git a/tuner/runtime/manifests/offline-grpo-worker-v1.json b/tuner/runtime/manifests/offline-grpo-worker-v1.json index 29b1d3cb..ef21c0e8 100644 --- a/tuner/runtime/manifests/offline-grpo-worker-v1.json +++ b/tuner/runtime/manifests/offline-grpo-worker-v1.json @@ -1 +1 @@ -{"closure_digest":"7e4d76575a0f3dfe650fc3bcf1ddf49195836be9d4f6262bbaabc0ca30b7f14d","closure_ref":"synaptic/offline-grpo-worker/v1","entrypoint":"tuner/runtime/packaged_grpo_child.py","member_count":131,"members":[{"git_mode":"100644","path":"SynthChat/__init__.py","sha256":"f9941fd4b7c15654b950b3d107ac9a0373d1ddfbe9dadcc72f8f608bfbcfa263","size_bytes":1583},{"git_mode":"100644","path":"SynthChat/config/__init__.py","sha256":"b9b4a831d9e625e63872dbd9d81fedf54064fdb2546c2cbdd3e9dde65c59a1e8","size_bytes":1162},{"git_mode":"100644","path":"SynthChat/config/config_loader.py","sha256":"09f80fd591fb9bf22afd712f613fa41d2f78b8e54f748c07d8e168082eed8fc1","size_bytes":9173},{"git_mode":"100644","path":"SynthChat/config/format_resolver.py","sha256":"264f6eeffe4068b554828e1cbc0c098eac9172dcd5fd0437c0e94a87a6c154c1","size_bytes":11651},{"git_mode":"100644","path":"SynthChat/utils/__init__.py","sha256":"0481a87cd831f9d77f6401fff01225e07ceb6295a99ce9db6391f6dfb86fc985","size_bytes":112},{"git_mode":"100644","path":"SynthChat/utils/prompt_renderer.py","sha256":"1f1479fe5ac9edeb33874422da79166abca9c582a34ebea6a532cccbd5aa15eb","size_bytes":1442},{"git_mode":"100644","path":"SynthChat/utils/yaml_loader.py","sha256":"125977de23485ea558d54744bf7bb8bb933aeae446eaae6be3f0ec09f1c8adbc","size_bytes":5264},{"git_mode":"100644","path":"Trainers/grpo/configs/config.yaml","sha256":"3346a0530959169e9d556e37b0342ba6ed93c2831164b6da225b3054d030b1e7","size_bytes":3546},{"git_mode":"100644","path":"Trainers/grpo/runtime_v1.py","sha256":"c254e10f9ccde3b0417b52f1d85d81551ca9c710be95cee577e8e63a025674bb","size_bytes":44654},{"git_mode":"100644","path":"Trainers/grpo/src/data_loader.py","sha256":"0b9357ce4239331219b2889715c2bef6d48ef0af73bf886636eda31bca92a9ba","size_bytes":4101},{"git_mode":"100644","path":"Trainers/grpo/src/model_loader.py","sha256":"78cb7b80be05bc3f16fd05866973cd4324eff9454acea89eacb30ad37776d00a","size_bytes":7927},{"git_mode":"100644","path":"Trainers/grpo/src/rewards.py","sha256":"e157997b56cf46f0fd270d75c43c4e6b3f4b7c9f8e1a03e5e90f1cec31c18578","size_bytes":23285},{"git_mode":"100644","path":"Trainers/sft/runtime_v1.py","sha256":"460121aaee3eb83764f67d9307cfb57a3b25c117611d987577b9631b6956a1b3","size_bytes":104495},{"git_mode":"100644","path":"shared/judge/__init__.py","sha256":"08fb40e578eae3a0716dcb66664aafc9b865a02739b8777a15ac9fcd51a41848","size_bytes":870},{"git_mode":"100644","path":"shared/judge/interaction_logger.py","sha256":"41ce3ea4be06b0fdd7f2d40e5ea8f8028022fe18f912d460ae06b4c37bdb2642","size_bytes":5573},{"git_mode":"100644","path":"shared/judge/judge_service.py","sha256":"87950058f236df537d5d2921f0d9e55c4eebdc9ffe46555a3874c23d9cb1e91e","size_bytes":17998},{"git_mode":"100644","path":"shared/judge/models.py","sha256":"0c1d18ab9e69c851596d9e2e23ffe7a73cf994ee106fa70b64b8bc698de3e591","size_bytes":8459},{"git_mode":"100644","path":"shared/judge/rubric_loader.py","sha256":"84584eb9f58e8e60e78dd174412bd7d6baae96a5772b2933fe020e54364065c3","size_bytes":4191},{"git_mode":"100644","path":"shared/judge/schema_builder.py","sha256":"523b0dafc8442d92fbb68b758723067be16a698968028a76e40b470e78f73f16","size_bytes":3062},{"git_mode":"100644","path":"shared/llm/__init__.py","sha256":"44b0b934213c3f1facda937ff5fd0fd97411fd335053ba9c178e230f9b06b4ed","size_bytes":1920},{"git_mode":"100644","path":"shared/llm/base.py","sha256":"bd0cbaf0f4edbc188e511ea8fd4401cc1ec3f937681ff126c992d228009ee61e","size_bytes":9185},{"git_mode":"100644","path":"shared/llm/config.py","sha256":"a3f95c1414898860fd40bd9f2a90fc209bb1b0955d0402cb6da67bbfaf1f1420","size_bytes":9822},{"git_mode":"100644","path":"shared/llm/exceptions.py","sha256":"7223e9c03f111b10c760b4c9d7c22df5958c855a5076de0d74872d1dc6ca83f2","size_bytes":589},{"git_mode":"100644","path":"shared/llm/factory.py","sha256":"11263dcc4803ffecdbbfd343a2dbf574bfd4a4efb51f60355699be77f7871b35","size_bytes":4583},{"git_mode":"100644","path":"shared/llm/providers/__init__.py","sha256":"e0c78fec1fa6ab46068760fe2f4ef9af4d6b2645adc80fe75bbdd06039d19138","size_bytes":310},{"git_mode":"100644","path":"shared/llm/providers/lmstudio.py","sha256":"64f70a0a1ccfd67467eb026d6be495881cd53eee1fc267ee6ea059ee8b1f6282","size_bytes":7871},{"git_mode":"100644","path":"shared/llm/providers/ollama.py","sha256":"9b646ce7631dfdb6243e2660a8257d84ab2711c2fe3cb5b408466ff4f1aa780d","size_bytes":7020},{"git_mode":"100644","path":"shared/llm/providers/openai_responses.py","sha256":"70ca5fa422f1a9a29ed153609c124237c8c6d2907885972948bd00c2a9b8e623","size_bytes":10346},{"git_mode":"100644","path":"shared/llm/providers/openrouter.py","sha256":"42276fbed027f1d98183e17ad7a37e87296fe57ea73125a687cb78738dd07dda","size_bytes":17541},{"git_mode":"100644","path":"shared/llm/providers/unsloth.py","sha256":"4d60fc82e70a8f8296dcd231a68754913a3de55b8148d26de831cc177f95b137","size_bytes":11619},{"git_mode":"100644","path":"shared/llm/usage.py","sha256":"9a9d27e8689304e58d1290445bfbbe27b476b437584c6357d894b53119bd91e6","size_bytes":6964},{"git_mode":"100644","path":"shared/ml/__init__.py","sha256":"ee72d716daaba9ee99ecc8be99d776fd695436c34c5768b3e83f51f407ccf71b","size_bytes":1262},{"git_mode":"100644","path":"shared/ml/retrieval_metrics.py","sha256":"67be37fea3634cca2e7c8f42c87f2e4c207714279cece0b1fe2e9f94088ebda0","size_bytes":11456},{"git_mode":"100644","path":"shared/model_loading/__init__.py","sha256":"19e82f3112b3685a95b2f8260c5f11862d649d23968cfa6eefe306319d95a51e","size_bytes":1755},{"git_mode":"100644","path":"shared/model_loading/merge.py","sha256":"e8f0f013076b276a0f722486cb6d7583c1f9b77cde4f716a4d838ef909406711","size_bytes":9529},{"git_mode":"100644","path":"shared/training_utils.py","sha256":"15136abdb987a7bdfbc8917c49f9c77ff553e91ce53890e239ea629241345a65","size_bytes":28814},{"git_mode":"100644","path":"shared/utilities/__init__.py","sha256":"546349addf2ba6c72661e185477354d743f6a42e8ef3bf8e78ed19a17b85e2ab","size_bytes":297},{"git_mode":"100644","path":"shared/utilities/env.py","sha256":"06f56639bc506d4e497ee79d4393d17d499bfd561fd0d0679c0ea7e103b8e886","size_bytes":3318},{"git_mode":"100644","path":"shared/utilities/paths.py","sha256":"3bfa1a13e4deef02cd91a920410e839f8ab0d34ec1081903cbcb742589e995b4","size_bytes":6843},{"git_mode":"100644","path":"shared/utilities/yaml_loader.py","sha256":"203996ff297e1714b0a3e40446f719e24b6d0e20eddb91bc528100b497d8591c","size_bytes":679},{"git_mode":"100644","path":"shared/validation/__init__.py","sha256":"2c80d0a16e9b08c649b131deec3e32cfe9260d127c3b6e9c4a6508eb730e0e7f","size_bytes":1828},{"git_mode":"100644","path":"shared/validation/fitness.py","sha256":"6d90c803c59afa2e1054f33eac06c28a0d765160b53c090fd8b3bad4568cea48","size_bytes":8227},{"git_mode":"100644","path":"shared/validation/parsing/__init__.py","sha256":"e04b52ae2417aabdf8f48d80e847d60de41ec094824d4f6d088aaa132209b6c2","size_bytes":1768},{"git_mode":"100644","path":"shared/validation/parsing/configured_formats.py","sha256":"2d69eff733771bd614a8c1e1252511041fdd4bd5a13cf565205e1ebfb52dc88c","size_bytes":8162},{"git_mode":"100644","path":"shared/validation/parsing/enums.py","sha256":"4ea46d5ef556cc9d5d93d75f944e5a5120effea5264ccd89318d06228beb82bf","size_bytes":1746},{"git_mode":"100644","path":"shared/validation/parsing/response_parser.py","sha256":"5968f7d4f9f70ca269643276a71c238d6543778a47222724a1db69090b8c3471","size_bytes":21535},{"git_mode":"100644","path":"shared/validation/parsing/tool_call_parser.py","sha256":"e786758dd896bf75ceb49ea46a2a37fbea6d13692d7a1a63a26ae2872e33017a","size_bytes":13943},{"git_mode":"100644","path":"shared/validation/parsing/utilities.py","sha256":"42b1c7cb98807601a46066d5073e4d3e1984f4c848f632e46371b55e25732aa2","size_bytes":3024},{"git_mode":"100644","path":"shared/validation/rollout_filters.py","sha256":"d412c53eacb8e7fe9c459345e3dbf83a65ac50fe413d58d567d41a9c7fa6fc86","size_bytes":18795},{"git_mode":"100644","path":"shared/validation/rubric/__init__.py","sha256":"7df886540f94034002142322a481672a991d3059ec503cb996ec03e42d3123f6","size_bytes":438},{"git_mode":"100644","path":"shared/validation/rubric/rubric_cache.py","sha256":"d428466ebdadafe0c8cd8febab5e1fa49414d846efeb45c70cba47102532c2ad","size_bytes":3121},{"git_mode":"100644","path":"shared/validation/rubric/rubric_loader.py","sha256":"f77ca87d02885899d61db49fe709cba445e5c6d35fccdadb1deb8e052d8141e6","size_bytes":2313},{"git_mode":"100644","path":"shared/validation/rubric/rubric_repository.py","sha256":"1173978ad22f34625e981dc2148525c87c7745a927ee0d81c93bc1a030f56676","size_bytes":7104},{"git_mode":"100644","path":"shared/validation/validators/__init__.py","sha256":"d86436b05056c97a9b0aaae4465d23d03f407e737f4b2098fe3fc274fca64035","size_bytes":1031},{"git_mode":"100644","path":"shared/validation/validators/base.py","sha256":"764ae0d8a1439a4823a7ce82fafd1b69406873549c847b65ed57ecb3da97a264","size_bytes":1564},{"git_mode":"100644","path":"shared/validation/validators/content/__init__.py","sha256":"ed370a9fdc34b511dcfe88053bee6e9d4f30e3f7de2263cd3c675b8021865bb2","size_bytes":600},{"git_mode":"100644","path":"shared/validation/validators/content/code_validator.py","sha256":"19ded618181d16de6fa3667a64cc80a96628fd6d0f39ee5412f22c3e7aa75842","size_bytes":1493},{"git_mode":"100644","path":"shared/validation/validators/content/json_validator.py","sha256":"dd4b6fb4cb51aabaac37c052df9ff23b5051fb575dba2e893458d3d64edba497","size_bytes":7524},{"git_mode":"100644","path":"shared/validation/validators/content/regex_validator.py","sha256":"bea1124ccc79fec327a24a61e96110696c3346529d303ddff4a56ecba34b1513","size_bytes":3148},{"git_mode":"100644","path":"shared/validation/validators/content/xml_validator.py","sha256":"970937f6d8d9ef61594e2a34a2804d999482bf319c237a1891db214250bedd2e","size_bytes":1056},{"git_mode":"100644","path":"shared/validation/validators/content/yaml_validator.py","sha256":"75571df0e7ac750feeb8f92dc7da5bdc4d085e032bcf94650d826ecae07d3219","size_bytes":3738},{"git_mode":"100644","path":"shared/validation/validators/cross_scope_validator.py","sha256":"27ed2f25b96cc6fecd5e0702fdbe98a8248caf1483952a0d2402d96cf56ee269","size_bytes":8323},{"git_mode":"100644","path":"shared/validation/validators/structure_validator.py","sha256":"d637fde147e89d052229f3951ac744b56d87eb3f8553626b61dfd36bbb9829cf","size_bytes":20391},{"git_mode":"100644","path":"shared/verifiers/__init__.py","sha256":"bcc0a64a5b1be388e3dc1e913492506cac4daeb4cb5f7a210bca5bd75bafc352","size_bytes":950},{"git_mode":"100644","path":"shared/verifiers/builtins/__init__.py","sha256":"209700d4779871de25567fb6daeabad23ef3b8791e2f6c2b10749c2beead9c88","size_bytes":777},{"git_mode":"100644","path":"shared/verifiers/builtins/args_match.py","sha256":"19f44e00d8dc78d93e85361c9ecd65790fb6fa31f740f9aab743d36e20f38a0f","size_bytes":14581},{"git_mode":"100644","path":"shared/verifiers/builtins/assertion_verifier.py","sha256":"3ab2e6983adf1c9774e665961466856025fca6905d82094e2f0bc48e67cdfc46","size_bytes":17366},{"git_mode":"100644","path":"shared/verifiers/builtins/audio_verifier.py","sha256":"51c75365d411ab77535e343d771178f16fd6fee133b3d815eab0302c783fd207","size_bytes":16786},{"git_mode":"100644","path":"shared/verifiers/builtins/llm_judge.py","sha256":"b4741581e4388ed43bce5fd4def1465abc9b1eab3b8b4905d7ff07f05dd80d15","size_bytes":6915},{"git_mode":"100644","path":"shared/verifiers/builtins/retrieval_verifier.py","sha256":"b70c804369efa4a76dedeb1aeb829814952fecaecee51352803f100f12ac938d","size_bytes":15290},{"git_mode":"100644","path":"shared/verifiers/builtins/structure.py","sha256":"3030f1ca99e9355b0b37a8772937318ef334857aae5c05e92ad3db1318a4dc26","size_bytes":2167},{"git_mode":"100644","path":"shared/verifiers/builtins/substring.py","sha256":"dd86a419aa78058f5e855a538cf63e8a48c3e757f0988139bae0f231c531d76a","size_bytes":3790},{"git_mode":"100644","path":"shared/verifiers/builtins/tool_sequence.py","sha256":"111cb44e290877f1f265fd3d6d9e3d6635501b06f3648613af4f4f716b9f8d56","size_bytes":6459},{"git_mode":"100644","path":"shared/verifiers/composite.py","sha256":"678dbdb3bbab21f4a4956b7e70e8ef2d5dc883ec500a1d2278103a1f892ef66c","size_bytes":5681},{"git_mode":"100644","path":"shared/verifiers/contract.py","sha256":"c594c5527aa949d02cdf8626d03a5ef958148d647a7d7b84df6ee5c6b6ce56cc","size_bytes":2367},{"git_mode":"100644","path":"shared/verifiers/extraction.py","sha256":"f4a8e734957abb1327eeca7faa7cf5b1eb6d9247180224315e4ea6a42bc2e867","size_bytes":6056},{"git_mode":"100644","path":"shared/verifiers/registry.py","sha256":"f55035cd60ee539e4577ae1e4f101ed4b42d6c4adfb5bc0e17ae415607a3db2b","size_bytes":2475},{"git_mode":"100644","path":"synaptic_tuner/__init__.py","sha256":"2462ff4ec439b77eb1ecfa901c1d8294628c853e9bbdf872de73dce9bfc0ae4d","size_bytes":134},{"git_mode":"100644","path":"synaptic_tuner/_version.py","sha256":"49fa86b62849d537e7e131445f272650ed77760db5f218f2113155ab60caf644","size_bytes":96},{"git_mode":"100644","path":"synaptic_tuner/api/__init__.py","sha256":"25c68ed273bf1e6b158021b98f8607276d980540a98b56a07801dccf9d6596a8","size_bytes":67},{"git_mode":"100644","path":"synaptic_tuner/api/v1/__init__.py","sha256":"4bc30420b18b3932183dcd0485892fa22f4f8e474ca3bb9b5e08adf3ae0fde3d","size_bytes":16893},{"git_mode":"100644","path":"synaptic_tuner/api/v1/_contract.py","sha256":"dcd6fc1ebdc75fbfd74ad4c6c96837b73209081258353557fedaaca76164673a","size_bytes":6702},{"git_mode":"100644","path":"synaptic_tuner/api/v1/_timestamps.py","sha256":"2b2ad7183f62bf1d42c4b9144d0315fe90c0e601649fb37f7778d017c632935d","size_bytes":698},{"git_mode":"100644","path":"synaptic_tuner/api/v1/observations.py","sha256":"b33f5826e6c03db889e0ff29c3eba65f5eab436b3d3f5146b8e4317828178d07","size_bytes":26569},{"git_mode":"100644","path":"synaptic_tuner/api/v1/planning.py","sha256":"e715b105a513fa56cfa510416e725471f8f3100cb34257be74bef3e40ca78460","size_bytes":9569},{"git_mode":"100644","path":"synaptic_tuner/api/v1/providers.py","sha256":"eca5efd5a6dee453543ae338ffddf9451d34ce1743e4ab4bde024b47be571ec0","size_bytes":4940},{"git_mode":"100644","path":"synaptic_tuner/api/v1/results.py","sha256":"65e530325c92c7afa18e3ca5f2ad9a11baf7d590221f08c33badfbf2d5f2c7b2","size_bytes":2236},{"git_mode":"100644","path":"synaptic_tuner/api/v1/training_facade.py","sha256":"bf094d0e152f5861bf8a8ff51962e0700123d9b43ed91d914b9e78bbea6facd9","size_bytes":11118},{"git_mode":"100644","path":"synaptic_tuner/api/v1/training_input.py","sha256":"209326325f8796ab7d038878e92def2c5521d7d98b7b9cc4dd1766d2e4f68c9d","size_bytes":38650},{"git_mode":"100644","path":"synaptic_tuner/api/v1/training_sources.py","sha256":"4c4ce3bc9d73e03d550c61f5ef8f18099137fe82a9be6ea96b226e7363e984b8","size_bytes":8454},{"git_mode":"100644","path":"synaptic_tuner/api/v1/usage.py","sha256":"33e36965e14ae52037f1da877c6606c46a40edf575e248769d5e5731b47b1307","size_bytes":6086},{"git_mode":"100644","path":"tuner/__init__.py","sha256":"920cf8a0ca993a712fbacf734621e1fdf14fdd2a7203701db120dc41e4815e7b","size_bytes":2666},{"git_mode":"100644","path":"tuner/cloud/__init__.py","sha256":"a9abb71aa9222c511524d3cc49b1cfe00f8938a6cb7c61792cf6135a6b82208b","size_bytes":3692},{"git_mode":"100644","path":"tuner/cloud/runtime_layout.py","sha256":"2a4cd1f5fc63d95bb3f0b5bc4f44e616bb0308d9509aceb198943953746ff705","size_bytes":9435},{"git_mode":"100644","path":"tuner/execution/__init__.py","sha256":"8193dfcd30c68a7d8ad02af0fb24f1a8120fce2a709decd132ab1acb33113d65","size_bytes":535},{"git_mode":"100644","path":"tuner/execution/contracts.py","sha256":"82d90f742e4e33d8071e557b5a872e0158684d94db5debdfbb85411954e4e638","size_bytes":32907},{"git_mode":"100644","path":"tuner/execution/evidence.py","sha256":"a04cc6843a794e62a702608d28c8e82d0c57af8227b3cb9743cf56b9605442ca","size_bytes":4634},{"git_mode":"100644","path":"tuner/execution/lifecycle.py","sha256":"4bc878038fc2384c0213a5f5c887aca8b15a67ed46b11e1acae1e2ecf0e97793","size_bytes":17660},{"git_mode":"100644","path":"tuner/execution/registry.py","sha256":"0cf424ff4b6bd628bae86b347f00a10acce8793cc41afc2ff1f3abbb738aec40","size_bytes":1515},{"git_mode":"100644","path":"tuner/execution/service.py","sha256":"a85e31e8a54479764463633a9c721b9af3afe38710f85efa35b9a5744ad33e4c","size_bytes":4514},{"git_mode":"100644","path":"tuner/project/__init__.py","sha256":"a858562f45452031f8a0e8ba4c9799f9784e09360a0258165dc3c4f89a46ca23","size_bytes":1513},{"git_mode":"100644","path":"tuner/project/config_layers.py","sha256":"0e06ddfb8b92faa106d052990df34f199c06c298c3a3a2366cab218f2c1731f1","size_bytes":6714},{"git_mode":"100644","path":"tuner/project/context.py","sha256":"147d08d5d6fcb3cf258ab347f070424e65e5c0c28e47ad1fd062e13cf56a8dd3","size_bytes":6518},{"git_mode":"100644","path":"tuner/project/errors.py","sha256":"6f1f94cd0b4e068a0cdee82259de0aee09353345e93caeb05272097a5e717444","size_bytes":1511},{"git_mode":"100644","path":"tuner/project/execution_source.py","sha256":"dc196dff10029c9a77b6d94fbfbcca45b39da4840bff40009b7152fd39d9622b","size_bytes":30595},{"git_mode":"100644","path":"tuner/project/git_verification.py","sha256":"0cfe00560c704eddbe688a0c7156c12f9acd2f321046017f50eb5d9e3bb61710","size_bytes":9767},{"git_mode":"100644","path":"tuner/project/manifest.py","sha256":"5506cf2ffb261988c0bbc5ffab1e466236bd10f503df5e805453f5ddf7d34be0","size_bytes":9498},{"git_mode":"100644","path":"tuner/project/path_refs.py","sha256":"35e47de6ed1c6096ba45e7ea64aedcc0f79aeb9425acb5041b14afb9d258c955","size_bytes":6287},{"git_mode":"100644","path":"tuner/project/secrets.py","sha256":"6f267060b0f24ab1517c4d4499fe54a993a77c2c78830c2d79415da871893818","size_bytes":4256},{"git_mode":"100644","path":"tuner/project/source_bundle.py","sha256":"16368658db727680f74326603e2d28f3ae9b5e481f7626c7be1a8280b588ecec","size_bytes":28339},{"git_mode":"100644","path":"tuner/runtime/__init__.py","sha256":"246429ce58a07dabeb05bf17967d3a7862014d114ee995a7419adcb27227286c","size_bytes":3566},{"git_mode":"100644","path":"tuner/runtime/artifacts.py","sha256":"c9eaefd6f6805af11d997256e14392c0c42173b852d94bfab90bcb20a1ae2cd2","size_bytes":6913},{"git_mode":"100644","path":"tuner/runtime/dispatch.py","sha256":"4c41faf87d7e620bdc281490bf3725550b83dacca8b15ea47bab253f669cdd8e","size_bytes":35463},{"git_mode":"100644","path":"tuner/runtime/offline_grpo_worker.py","sha256":"aac831048855e959598a4dc45b29e614f06f8714df18e868fd6b069e3c765710","size_bytes":9323},{"git_mode":"100644","path":"tuner/runtime/offline_sft_worker.py","sha256":"248f258254bb9c7021eed2ee0ff32d9633a021e656c10efb15825e7f6aaa9366","size_bytes":32592},{"git_mode":"100644","path":"tuner/runtime/packaged_grpo_child.py","sha256":"124d5b2a969d38ac2e488d58a216759108f1a55dfcc3dcaf1012791efe792e4b","size_bytes":8273},{"git_mode":"100644","path":"tuner/runtime/packaged_grpo_execution.py","sha256":"a77c7d6b97ad2ded3b9631174e479e60462f1894513f90fd2fc7798828c6867d","size_bytes":14943},{"git_mode":"100644","path":"tuner/runtime/packaged_grpo_verification.py","sha256":"56564c6ee6414a3a71ce7c9ef330152d812fede2a711a2329463a0b81a61b0d6","size_bytes":10319},{"git_mode":"100644","path":"tuner/runtime/packaged_grpo_worker_closure.py","sha256":"81da8db5a61e9a874234d1e50c529e1056282fd0eef133b2720eb0f34f78cddc","size_bytes":3735},{"git_mode":"100644","path":"tuner/runtime/packaged_sft_child.py","sha256":"ab41eb7dca28b440c043f589d297dc0d9f718ed14f6cb36c96d7bd1b7bfcdc14","size_bytes":23556},{"git_mode":"100644","path":"tuner/runtime/packaged_sft_execution.py","sha256":"cab2b5032d16146422398305120f52c5a124287ecf929b640a1f17d0ea2ed648","size_bytes":80053},{"git_mode":"100644","path":"tuner/runtime/packaged_training_worker.py","sha256":"9cfe13dc9f0124b4da01d05e92cf3c2c9249d075c3a508d6b93b0a7c7f0e109f","size_bytes":34986},{"git_mode":"100644","path":"tuner/runtime/packaged_worker_closure.py","sha256":"e457937426e19747e867a314c1a2556ee64bd5cbc19359f535253b02403d1443","size_bytes":8705},{"git_mode":"100644","path":"tuner/runtime/releases.py","sha256":"c0aa7ba37e8d35287fdd480cf0c76053a554e546c40c8bf36f5cf05d1a96d22b","size_bytes":52535},{"git_mode":"100644","path":"tuner/runtime/verification.py","sha256":"4c2314b20cbf7937a3f24aa90676000b9e21f0ab6701b2c1225a53e5596bf4a0","size_bytes":53856},{"git_mode":"100644","path":"tuner/training/__init__.py","sha256":"23ca59d6203276c6160e0bb57dbd5c43cf385c6aec60b4e3e08b93d1d501cb97","size_bytes":2995},{"git_mode":"100644","path":"tuner/training/contracts.py","sha256":"91c4d66d9810cc0471981c3d4ef4b54feaed5b92eb41a468f31201f8111303b5","size_bytes":21504},{"git_mode":"100644","path":"tuner/training/grpo_compilation.py","sha256":"c6268486375bbc16f1888bb43d07b93a8de280d000a774914d59d7fd05ca5cc0","size_bytes":8998},{"git_mode":"100644","path":"tuner/training/packaged_compilation.py","sha256":"477caede6902fc587af680889b93fd9e0e42493d1a6653f3cd7413a9c7cadde8","size_bytes":9413},{"git_mode":"100644","path":"tuner/training/packaged_grpo_compilation.py","sha256":"d186a05ffb8cc756e782b2bb9a3f66e925779104b70045bd56a5592846fec524","size_bytes":8722},{"git_mode":"100644","path":"tuner/training/recipes.py","sha256":"bb844ed4883f00ccea9936eebad50c466e6e4bc148e88aa680d93121edf67dcc","size_bytes":6300}],"optional_features":[],"owned_module_prefixes":["tuner","synaptic_tuner","Trainers","shared","SynthChat","Evaluator","MechInterp","configs","src"],"payload_bytes":1404321,"schema_version":"synaptic-offline-grpo-worker-closure/v1","trainer_entrypoint":"Trainers/grpo/runtime_v1.py"} +{"closure_digest":"bfcc29f41d774cb393545417e5a43751776c16aa2efd9bcd39b09371d609ba87","closure_ref":"synaptic/offline-grpo-worker/v1","entrypoint":"tuner/runtime/packaged_grpo_child.py","member_count":131,"members":[{"git_mode":"100644","path":"SynthChat/__init__.py","sha256":"f9941fd4b7c15654b950b3d107ac9a0373d1ddfbe9dadcc72f8f608bfbcfa263","size_bytes":1583},{"git_mode":"100644","path":"SynthChat/config/__init__.py","sha256":"b9b4a831d9e625e63872dbd9d81fedf54064fdb2546c2cbdd3e9dde65c59a1e8","size_bytes":1162},{"git_mode":"100644","path":"SynthChat/config/config_loader.py","sha256":"09f80fd591fb9bf22afd712f613fa41d2f78b8e54f748c07d8e168082eed8fc1","size_bytes":9173},{"git_mode":"100644","path":"SynthChat/config/format_resolver.py","sha256":"264f6eeffe4068b554828e1cbc0c098eac9172dcd5fd0437c0e94a87a6c154c1","size_bytes":11651},{"git_mode":"100644","path":"SynthChat/utils/__init__.py","sha256":"0481a87cd831f9d77f6401fff01225e07ceb6295a99ce9db6391f6dfb86fc985","size_bytes":112},{"git_mode":"100644","path":"SynthChat/utils/prompt_renderer.py","sha256":"1f1479fe5ac9edeb33874422da79166abca9c582a34ebea6a532cccbd5aa15eb","size_bytes":1442},{"git_mode":"100644","path":"SynthChat/utils/yaml_loader.py","sha256":"125977de23485ea558d54744bf7bb8bb933aeae446eaae6be3f0ec09f1c8adbc","size_bytes":5264},{"git_mode":"100644","path":"Trainers/grpo/configs/config.yaml","sha256":"3346a0530959169e9d556e37b0342ba6ed93c2831164b6da225b3054d030b1e7","size_bytes":3546},{"git_mode":"100644","path":"Trainers/grpo/runtime_v1.py","sha256":"d71e2c2d68753d4ebc319ecbffd0676812278d32ad8e827e5361af1dafd94c72","size_bytes":45029},{"git_mode":"100644","path":"Trainers/grpo/src/data_loader.py","sha256":"0b9357ce4239331219b2889715c2bef6d48ef0af73bf886636eda31bca92a9ba","size_bytes":4101},{"git_mode":"100644","path":"Trainers/grpo/src/model_loader.py","sha256":"78cb7b80be05bc3f16fd05866973cd4324eff9454acea89eacb30ad37776d00a","size_bytes":7927},{"git_mode":"100644","path":"Trainers/grpo/src/rewards.py","sha256":"e157997b56cf46f0fd270d75c43c4e6b3f4b7c9f8e1a03e5e90f1cec31c18578","size_bytes":23285},{"git_mode":"100644","path":"Trainers/sft/runtime_v1.py","sha256":"460121aaee3eb83764f67d9307cfb57a3b25c117611d987577b9631b6956a1b3","size_bytes":104495},{"git_mode":"100644","path":"shared/judge/__init__.py","sha256":"08fb40e578eae3a0716dcb66664aafc9b865a02739b8777a15ac9fcd51a41848","size_bytes":870},{"git_mode":"100644","path":"shared/judge/interaction_logger.py","sha256":"41ce3ea4be06b0fdd7f2d40e5ea8f8028022fe18f912d460ae06b4c37bdb2642","size_bytes":5573},{"git_mode":"100644","path":"shared/judge/judge_service.py","sha256":"87950058f236df537d5d2921f0d9e55c4eebdc9ffe46555a3874c23d9cb1e91e","size_bytes":17998},{"git_mode":"100644","path":"shared/judge/models.py","sha256":"0c1d18ab9e69c851596d9e2e23ffe7a73cf994ee106fa70b64b8bc698de3e591","size_bytes":8459},{"git_mode":"100644","path":"shared/judge/rubric_loader.py","sha256":"84584eb9f58e8e60e78dd174412bd7d6baae96a5772b2933fe020e54364065c3","size_bytes":4191},{"git_mode":"100644","path":"shared/judge/schema_builder.py","sha256":"523b0dafc8442d92fbb68b758723067be16a698968028a76e40b470e78f73f16","size_bytes":3062},{"git_mode":"100644","path":"shared/llm/__init__.py","sha256":"44b0b934213c3f1facda937ff5fd0fd97411fd335053ba9c178e230f9b06b4ed","size_bytes":1920},{"git_mode":"100644","path":"shared/llm/base.py","sha256":"bd0cbaf0f4edbc188e511ea8fd4401cc1ec3f937681ff126c992d228009ee61e","size_bytes":9185},{"git_mode":"100644","path":"shared/llm/config.py","sha256":"a3f95c1414898860fd40bd9f2a90fc209bb1b0955d0402cb6da67bbfaf1f1420","size_bytes":9822},{"git_mode":"100644","path":"shared/llm/exceptions.py","sha256":"7223e9c03f111b10c760b4c9d7c22df5958c855a5076de0d74872d1dc6ca83f2","size_bytes":589},{"git_mode":"100644","path":"shared/llm/factory.py","sha256":"11263dcc4803ffecdbbfd343a2dbf574bfd4a4efb51f60355699be77f7871b35","size_bytes":4583},{"git_mode":"100644","path":"shared/llm/providers/__init__.py","sha256":"e0c78fec1fa6ab46068760fe2f4ef9af4d6b2645adc80fe75bbdd06039d19138","size_bytes":310},{"git_mode":"100644","path":"shared/llm/providers/lmstudio.py","sha256":"64f70a0a1ccfd67467eb026d6be495881cd53eee1fc267ee6ea059ee8b1f6282","size_bytes":7871},{"git_mode":"100644","path":"shared/llm/providers/ollama.py","sha256":"9b646ce7631dfdb6243e2660a8257d84ab2711c2fe3cb5b408466ff4f1aa780d","size_bytes":7020},{"git_mode":"100644","path":"shared/llm/providers/openai_responses.py","sha256":"70ca5fa422f1a9a29ed153609c124237c8c6d2907885972948bd00c2a9b8e623","size_bytes":10346},{"git_mode":"100644","path":"shared/llm/providers/openrouter.py","sha256":"42276fbed027f1d98183e17ad7a37e87296fe57ea73125a687cb78738dd07dda","size_bytes":17541},{"git_mode":"100644","path":"shared/llm/providers/unsloth.py","sha256":"4d60fc82e70a8f8296dcd231a68754913a3de55b8148d26de831cc177f95b137","size_bytes":11619},{"git_mode":"100644","path":"shared/llm/usage.py","sha256":"9a9d27e8689304e58d1290445bfbbe27b476b437584c6357d894b53119bd91e6","size_bytes":6964},{"git_mode":"100644","path":"shared/ml/__init__.py","sha256":"ee72d716daaba9ee99ecc8be99d776fd695436c34c5768b3e83f51f407ccf71b","size_bytes":1262},{"git_mode":"100644","path":"shared/ml/retrieval_metrics.py","sha256":"67be37fea3634cca2e7c8f42c87f2e4c207714279cece0b1fe2e9f94088ebda0","size_bytes":11456},{"git_mode":"100644","path":"shared/model_loading/__init__.py","sha256":"19e82f3112b3685a95b2f8260c5f11862d649d23968cfa6eefe306319d95a51e","size_bytes":1755},{"git_mode":"100644","path":"shared/model_loading/merge.py","sha256":"e8f0f013076b276a0f722486cb6d7583c1f9b77cde4f716a4d838ef909406711","size_bytes":9529},{"git_mode":"100644","path":"shared/training_utils.py","sha256":"15136abdb987a7bdfbc8917c49f9c77ff553e91ce53890e239ea629241345a65","size_bytes":28814},{"git_mode":"100644","path":"shared/utilities/__init__.py","sha256":"546349addf2ba6c72661e185477354d743f6a42e8ef3bf8e78ed19a17b85e2ab","size_bytes":297},{"git_mode":"100644","path":"shared/utilities/env.py","sha256":"06f56639bc506d4e497ee79d4393d17d499bfd561fd0d0679c0ea7e103b8e886","size_bytes":3318},{"git_mode":"100644","path":"shared/utilities/paths.py","sha256":"3bfa1a13e4deef02cd91a920410e839f8ab0d34ec1081903cbcb742589e995b4","size_bytes":6843},{"git_mode":"100644","path":"shared/utilities/yaml_loader.py","sha256":"203996ff297e1714b0a3e40446f719e24b6d0e20eddb91bc528100b497d8591c","size_bytes":679},{"git_mode":"100644","path":"shared/validation/__init__.py","sha256":"2c80d0a16e9b08c649b131deec3e32cfe9260d127c3b6e9c4a6508eb730e0e7f","size_bytes":1828},{"git_mode":"100644","path":"shared/validation/fitness.py","sha256":"6d90c803c59afa2e1054f33eac06c28a0d765160b53c090fd8b3bad4568cea48","size_bytes":8227},{"git_mode":"100644","path":"shared/validation/parsing/__init__.py","sha256":"e04b52ae2417aabdf8f48d80e847d60de41ec094824d4f6d088aaa132209b6c2","size_bytes":1768},{"git_mode":"100644","path":"shared/validation/parsing/configured_formats.py","sha256":"2d69eff733771bd614a8c1e1252511041fdd4bd5a13cf565205e1ebfb52dc88c","size_bytes":8162},{"git_mode":"100644","path":"shared/validation/parsing/enums.py","sha256":"4ea46d5ef556cc9d5d93d75f944e5a5120effea5264ccd89318d06228beb82bf","size_bytes":1746},{"git_mode":"100644","path":"shared/validation/parsing/response_parser.py","sha256":"5968f7d4f9f70ca269643276a71c238d6543778a47222724a1db69090b8c3471","size_bytes":21535},{"git_mode":"100644","path":"shared/validation/parsing/tool_call_parser.py","sha256":"e786758dd896bf75ceb49ea46a2a37fbea6d13692d7a1a63a26ae2872e33017a","size_bytes":13943},{"git_mode":"100644","path":"shared/validation/parsing/utilities.py","sha256":"42b1c7cb98807601a46066d5073e4d3e1984f4c848f632e46371b55e25732aa2","size_bytes":3024},{"git_mode":"100644","path":"shared/validation/rollout_filters.py","sha256":"d412c53eacb8e7fe9c459345e3dbf83a65ac50fe413d58d567d41a9c7fa6fc86","size_bytes":18795},{"git_mode":"100644","path":"shared/validation/rubric/__init__.py","sha256":"7df886540f94034002142322a481672a991d3059ec503cb996ec03e42d3123f6","size_bytes":438},{"git_mode":"100644","path":"shared/validation/rubric/rubric_cache.py","sha256":"d428466ebdadafe0c8cd8febab5e1fa49414d846efeb45c70cba47102532c2ad","size_bytes":3121},{"git_mode":"100644","path":"shared/validation/rubric/rubric_loader.py","sha256":"f77ca87d02885899d61db49fe709cba445e5c6d35fccdadb1deb8e052d8141e6","size_bytes":2313},{"git_mode":"100644","path":"shared/validation/rubric/rubric_repository.py","sha256":"1173978ad22f34625e981dc2148525c87c7745a927ee0d81c93bc1a030f56676","size_bytes":7104},{"git_mode":"100644","path":"shared/validation/validators/__init__.py","sha256":"d86436b05056c97a9b0aaae4465d23d03f407e737f4b2098fe3fc274fca64035","size_bytes":1031},{"git_mode":"100644","path":"shared/validation/validators/base.py","sha256":"764ae0d8a1439a4823a7ce82fafd1b69406873549c847b65ed57ecb3da97a264","size_bytes":1564},{"git_mode":"100644","path":"shared/validation/validators/content/__init__.py","sha256":"ed370a9fdc34b511dcfe88053bee6e9d4f30e3f7de2263cd3c675b8021865bb2","size_bytes":600},{"git_mode":"100644","path":"shared/validation/validators/content/code_validator.py","sha256":"19ded618181d16de6fa3667a64cc80a96628fd6d0f39ee5412f22c3e7aa75842","size_bytes":1493},{"git_mode":"100644","path":"shared/validation/validators/content/json_validator.py","sha256":"dd4b6fb4cb51aabaac37c052df9ff23b5051fb575dba2e893458d3d64edba497","size_bytes":7524},{"git_mode":"100644","path":"shared/validation/validators/content/regex_validator.py","sha256":"bea1124ccc79fec327a24a61e96110696c3346529d303ddff4a56ecba34b1513","size_bytes":3148},{"git_mode":"100644","path":"shared/validation/validators/content/xml_validator.py","sha256":"970937f6d8d9ef61594e2a34a2804d999482bf319c237a1891db214250bedd2e","size_bytes":1056},{"git_mode":"100644","path":"shared/validation/validators/content/yaml_validator.py","sha256":"75571df0e7ac750feeb8f92dc7da5bdc4d085e032bcf94650d826ecae07d3219","size_bytes":3738},{"git_mode":"100644","path":"shared/validation/validators/cross_scope_validator.py","sha256":"27ed2f25b96cc6fecd5e0702fdbe98a8248caf1483952a0d2402d96cf56ee269","size_bytes":8323},{"git_mode":"100644","path":"shared/validation/validators/structure_validator.py","sha256":"d637fde147e89d052229f3951ac744b56d87eb3f8553626b61dfd36bbb9829cf","size_bytes":20391},{"git_mode":"100644","path":"shared/verifiers/__init__.py","sha256":"bcc0a64a5b1be388e3dc1e913492506cac4daeb4cb5f7a210bca5bd75bafc352","size_bytes":950},{"git_mode":"100644","path":"shared/verifiers/builtins/__init__.py","sha256":"209700d4779871de25567fb6daeabad23ef3b8791e2f6c2b10749c2beead9c88","size_bytes":777},{"git_mode":"100644","path":"shared/verifiers/builtins/args_match.py","sha256":"19f44e00d8dc78d93e85361c9ecd65790fb6fa31f740f9aab743d36e20f38a0f","size_bytes":14581},{"git_mode":"100644","path":"shared/verifiers/builtins/assertion_verifier.py","sha256":"3ab2e6983adf1c9774e665961466856025fca6905d82094e2f0bc48e67cdfc46","size_bytes":17366},{"git_mode":"100644","path":"shared/verifiers/builtins/audio_verifier.py","sha256":"51c75365d411ab77535e343d771178f16fd6fee133b3d815eab0302c783fd207","size_bytes":16786},{"git_mode":"100644","path":"shared/verifiers/builtins/llm_judge.py","sha256":"b4741581e4388ed43bce5fd4def1465abc9b1eab3b8b4905d7ff07f05dd80d15","size_bytes":6915},{"git_mode":"100644","path":"shared/verifiers/builtins/retrieval_verifier.py","sha256":"b70c804369efa4a76dedeb1aeb829814952fecaecee51352803f100f12ac938d","size_bytes":15290},{"git_mode":"100644","path":"shared/verifiers/builtins/structure.py","sha256":"3030f1ca99e9355b0b37a8772937318ef334857aae5c05e92ad3db1318a4dc26","size_bytes":2167},{"git_mode":"100644","path":"shared/verifiers/builtins/substring.py","sha256":"dd86a419aa78058f5e855a538cf63e8a48c3e757f0988139bae0f231c531d76a","size_bytes":3790},{"git_mode":"100644","path":"shared/verifiers/builtins/tool_sequence.py","sha256":"111cb44e290877f1f265fd3d6d9e3d6635501b06f3648613af4f4f716b9f8d56","size_bytes":6459},{"git_mode":"100644","path":"shared/verifiers/composite.py","sha256":"678dbdb3bbab21f4a4956b7e70e8ef2d5dc883ec500a1d2278103a1f892ef66c","size_bytes":5681},{"git_mode":"100644","path":"shared/verifiers/contract.py","sha256":"c594c5527aa949d02cdf8626d03a5ef958148d647a7d7b84df6ee5c6b6ce56cc","size_bytes":2367},{"git_mode":"100644","path":"shared/verifiers/extraction.py","sha256":"f4a8e734957abb1327eeca7faa7cf5b1eb6d9247180224315e4ea6a42bc2e867","size_bytes":6056},{"git_mode":"100644","path":"shared/verifiers/registry.py","sha256":"f55035cd60ee539e4577ae1e4f101ed4b42d6c4adfb5bc0e17ae415607a3db2b","size_bytes":2475},{"git_mode":"100644","path":"synaptic_tuner/__init__.py","sha256":"2462ff4ec439b77eb1ecfa901c1d8294628c853e9bbdf872de73dce9bfc0ae4d","size_bytes":134},{"git_mode":"100644","path":"synaptic_tuner/_version.py","sha256":"49fa86b62849d537e7e131445f272650ed77760db5f218f2113155ab60caf644","size_bytes":96},{"git_mode":"100644","path":"synaptic_tuner/api/__init__.py","sha256":"25c68ed273bf1e6b158021b98f8607276d980540a98b56a07801dccf9d6596a8","size_bytes":67},{"git_mode":"100644","path":"synaptic_tuner/api/v1/__init__.py","sha256":"4bc30420b18b3932183dcd0485892fa22f4f8e474ca3bb9b5e08adf3ae0fde3d","size_bytes":16893},{"git_mode":"100644","path":"synaptic_tuner/api/v1/_contract.py","sha256":"dcd6fc1ebdc75fbfd74ad4c6c96837b73209081258353557fedaaca76164673a","size_bytes":6702},{"git_mode":"100644","path":"synaptic_tuner/api/v1/_timestamps.py","sha256":"2b2ad7183f62bf1d42c4b9144d0315fe90c0e601649fb37f7778d017c632935d","size_bytes":698},{"git_mode":"100644","path":"synaptic_tuner/api/v1/observations.py","sha256":"b33f5826e6c03db889e0ff29c3eba65f5eab436b3d3f5146b8e4317828178d07","size_bytes":26569},{"git_mode":"100644","path":"synaptic_tuner/api/v1/planning.py","sha256":"e715b105a513fa56cfa510416e725471f8f3100cb34257be74bef3e40ca78460","size_bytes":9569},{"git_mode":"100644","path":"synaptic_tuner/api/v1/providers.py","sha256":"eca5efd5a6dee453543ae338ffddf9451d34ce1743e4ab4bde024b47be571ec0","size_bytes":4940},{"git_mode":"100644","path":"synaptic_tuner/api/v1/results.py","sha256":"65e530325c92c7afa18e3ca5f2ad9a11baf7d590221f08c33badfbf2d5f2c7b2","size_bytes":2236},{"git_mode":"100644","path":"synaptic_tuner/api/v1/training_facade.py","sha256":"bf094d0e152f5861bf8a8ff51962e0700123d9b43ed91d914b9e78bbea6facd9","size_bytes":11118},{"git_mode":"100644","path":"synaptic_tuner/api/v1/training_input.py","sha256":"209326325f8796ab7d038878e92def2c5521d7d98b7b9cc4dd1766d2e4f68c9d","size_bytes":38650},{"git_mode":"100644","path":"synaptic_tuner/api/v1/training_sources.py","sha256":"4c4ce3bc9d73e03d550c61f5ef8f18099137fe82a9be6ea96b226e7363e984b8","size_bytes":8454},{"git_mode":"100644","path":"synaptic_tuner/api/v1/usage.py","sha256":"33e36965e14ae52037f1da877c6606c46a40edf575e248769d5e5731b47b1307","size_bytes":6086},{"git_mode":"100644","path":"tuner/__init__.py","sha256":"920cf8a0ca993a712fbacf734621e1fdf14fdd2a7203701db120dc41e4815e7b","size_bytes":2666},{"git_mode":"100644","path":"tuner/cloud/__init__.py","sha256":"a9abb71aa9222c511524d3cc49b1cfe00f8938a6cb7c61792cf6135a6b82208b","size_bytes":3692},{"git_mode":"100644","path":"tuner/cloud/runtime_layout.py","sha256":"2a4cd1f5fc63d95bb3f0b5bc4f44e616bb0308d9509aceb198943953746ff705","size_bytes":9435},{"git_mode":"100644","path":"tuner/execution/__init__.py","sha256":"8193dfcd30c68a7d8ad02af0fb24f1a8120fce2a709decd132ab1acb33113d65","size_bytes":535},{"git_mode":"100644","path":"tuner/execution/contracts.py","sha256":"82d90f742e4e33d8071e557b5a872e0158684d94db5debdfbb85411954e4e638","size_bytes":32907},{"git_mode":"100644","path":"tuner/execution/evidence.py","sha256":"a04cc6843a794e62a702608d28c8e82d0c57af8227b3cb9743cf56b9605442ca","size_bytes":4634},{"git_mode":"100644","path":"tuner/execution/lifecycle.py","sha256":"4bc878038fc2384c0213a5f5c887aca8b15a67ed46b11e1acae1e2ecf0e97793","size_bytes":17660},{"git_mode":"100644","path":"tuner/execution/registry.py","sha256":"0cf424ff4b6bd628bae86b347f00a10acce8793cc41afc2ff1f3abbb738aec40","size_bytes":1515},{"git_mode":"100644","path":"tuner/execution/service.py","sha256":"a85e31e8a54479764463633a9c721b9af3afe38710f85efa35b9a5744ad33e4c","size_bytes":4514},{"git_mode":"100644","path":"tuner/project/__init__.py","sha256":"a858562f45452031f8a0e8ba4c9799f9784e09360a0258165dc3c4f89a46ca23","size_bytes":1513},{"git_mode":"100644","path":"tuner/project/config_layers.py","sha256":"0e06ddfb8b92faa106d052990df34f199c06c298c3a3a2366cab218f2c1731f1","size_bytes":6714},{"git_mode":"100644","path":"tuner/project/context.py","sha256":"147d08d5d6fcb3cf258ab347f070424e65e5c0c28e47ad1fd062e13cf56a8dd3","size_bytes":6518},{"git_mode":"100644","path":"tuner/project/errors.py","sha256":"6f1f94cd0b4e068a0cdee82259de0aee09353345e93caeb05272097a5e717444","size_bytes":1511},{"git_mode":"100644","path":"tuner/project/execution_source.py","sha256":"dc196dff10029c9a77b6d94fbfbcca45b39da4840bff40009b7152fd39d9622b","size_bytes":30595},{"git_mode":"100644","path":"tuner/project/git_verification.py","sha256":"0cfe00560c704eddbe688a0c7156c12f9acd2f321046017f50eb5d9e3bb61710","size_bytes":9767},{"git_mode":"100644","path":"tuner/project/manifest.py","sha256":"5506cf2ffb261988c0bbc5ffab1e466236bd10f503df5e805453f5ddf7d34be0","size_bytes":9498},{"git_mode":"100644","path":"tuner/project/path_refs.py","sha256":"35e47de6ed1c6096ba45e7ea64aedcc0f79aeb9425acb5041b14afb9d258c955","size_bytes":6287},{"git_mode":"100644","path":"tuner/project/secrets.py","sha256":"6f267060b0f24ab1517c4d4499fe54a993a77c2c78830c2d79415da871893818","size_bytes":4256},{"git_mode":"100644","path":"tuner/project/source_bundle.py","sha256":"16368658db727680f74326603e2d28f3ae9b5e481f7626c7be1a8280b588ecec","size_bytes":28339},{"git_mode":"100644","path":"tuner/runtime/__init__.py","sha256":"246429ce58a07dabeb05bf17967d3a7862014d114ee995a7419adcb27227286c","size_bytes":3566},{"git_mode":"100644","path":"tuner/runtime/artifacts.py","sha256":"c9eaefd6f6805af11d997256e14392c0c42173b852d94bfab90bcb20a1ae2cd2","size_bytes":6913},{"git_mode":"100644","path":"tuner/runtime/dispatch.py","sha256":"4c41faf87d7e620bdc281490bf3725550b83dacca8b15ea47bab253f669cdd8e","size_bytes":35463},{"git_mode":"100644","path":"tuner/runtime/offline_grpo_worker.py","sha256":"aac831048855e959598a4dc45b29e614f06f8714df18e868fd6b069e3c765710","size_bytes":9323},{"git_mode":"100644","path":"tuner/runtime/offline_sft_worker.py","sha256":"2304eeb00667954510c34cc4aacaa818532a301fbeabf04c2fb165d6cd1d70b5","size_bytes":36343},{"git_mode":"100644","path":"tuner/runtime/packaged_grpo_child.py","sha256":"124d5b2a969d38ac2e488d58a216759108f1a55dfcc3dcaf1012791efe792e4b","size_bytes":8273},{"git_mode":"100644","path":"tuner/runtime/packaged_grpo_execution.py","sha256":"a77c7d6b97ad2ded3b9631174e479e60462f1894513f90fd2fc7798828c6867d","size_bytes":14943},{"git_mode":"100644","path":"tuner/runtime/packaged_grpo_verification.py","sha256":"56564c6ee6414a3a71ce7c9ef330152d812fede2a711a2329463a0b81a61b0d6","size_bytes":10319},{"git_mode":"100644","path":"tuner/runtime/packaged_grpo_worker_closure.py","sha256":"81da8db5a61e9a874234d1e50c529e1056282fd0eef133b2720eb0f34f78cddc","size_bytes":3735},{"git_mode":"100644","path":"tuner/runtime/packaged_sft_child.py","sha256":"ab41eb7dca28b440c043f589d297dc0d9f718ed14f6cb36c96d7bd1b7bfcdc14","size_bytes":23556},{"git_mode":"100644","path":"tuner/runtime/packaged_sft_execution.py","sha256":"cab2b5032d16146422398305120f52c5a124287ecf929b640a1f17d0ea2ed648","size_bytes":80053},{"git_mode":"100644","path":"tuner/runtime/packaged_training_worker.py","sha256":"9cfe13dc9f0124b4da01d05e92cf3c2c9249d075c3a508d6b93b0a7c7f0e109f","size_bytes":34986},{"git_mode":"100644","path":"tuner/runtime/packaged_worker_closure.py","sha256":"e457937426e19747e867a314c1a2556ee64bd5cbc19359f535253b02403d1443","size_bytes":8705},{"git_mode":"100644","path":"tuner/runtime/releases.py","sha256":"c0aa7ba37e8d35287fdd480cf0c76053a554e546c40c8bf36f5cf05d1a96d22b","size_bytes":52535},{"git_mode":"100644","path":"tuner/runtime/verification.py","sha256":"4c2314b20cbf7937a3f24aa90676000b9e21f0ab6701b2c1225a53e5596bf4a0","size_bytes":53856},{"git_mode":"100644","path":"tuner/training/__init__.py","sha256":"23ca59d6203276c6160e0bb57dbd5c43cf385c6aec60b4e3e08b93d1d501cb97","size_bytes":2995},{"git_mode":"100644","path":"tuner/training/contracts.py","sha256":"91c4d66d9810cc0471981c3d4ef4b54feaed5b92eb41a468f31201f8111303b5","size_bytes":21504},{"git_mode":"100644","path":"tuner/training/grpo_compilation.py","sha256":"c6268486375bbc16f1888bb43d07b93a8de280d000a774914d59d7fd05ca5cc0","size_bytes":8998},{"git_mode":"100644","path":"tuner/training/packaged_compilation.py","sha256":"477caede6902fc587af680889b93fd9e0e42493d1a6653f3cd7413a9c7cadde8","size_bytes":9413},{"git_mode":"100644","path":"tuner/training/packaged_grpo_compilation.py","sha256":"d186a05ffb8cc756e782b2bb9a3f66e925779104b70045bd56a5592846fec524","size_bytes":8722},{"git_mode":"100644","path":"tuner/training/recipes.py","sha256":"bb844ed4883f00ccea9936eebad50c466e6e4bc148e88aa680d93121edf67dcc","size_bytes":6300}],"optional_features":[],"owned_module_prefixes":["tuner","synaptic_tuner","Trainers","shared","SynthChat","Evaluator","MechInterp","configs","src"],"payload_bytes":1408447,"schema_version":"synaptic-offline-grpo-worker-closure/v1","trainer_entrypoint":"Trainers/grpo/runtime_v1.py"} diff --git a/tuner/runtime/manifests/offline-sft-worker-v1.json b/tuner/runtime/manifests/offline-sft-worker-v1.json index d840061b..ed73ccfa 100644 --- a/tuner/runtime/manifests/offline-sft-worker-v1.json +++ b/tuner/runtime/manifests/offline-sft-worker-v1.json @@ -1 +1 @@ -{"closure_digest":"f4d22ba18f3e7b86a20aa0fe541fa1040844c5a921955928236f81db7c1d465c","closure_ref":"synaptic/offline-sft-worker/v1","entrypoint":"Trainers/sft/runtime_v1.py","member_count":68,"members":[{"git_mode":"100644","path":"Trainers/sft/configs/config.yaml","sha256":"93b85493fa4200a103267ca7ba1223dcd519be3d0cf9017304ebcdc2e7a99ec1","size_bytes":5422},{"git_mode":"100644","path":"Trainers/sft/configs/config_loader.py","sha256":"4f90bf152d191544f9a567e22f41a171aa199e8995a6a7ea00a7098bc44a4060","size_bytes":19126},{"git_mode":"100644","path":"Trainers/sft/runtime_v1.py","sha256":"460121aaee3eb83764f67d9307cfb57a3b25c117611d987577b9631b6956a1b3","size_bytes":104495},{"git_mode":"100644","path":"Trainers/sft/src/data_loader.py","sha256":"34d664535b4d26f9a6e9b1a4121e37b575f3960ea39b6fd651f2fe9b624ffb09","size_bytes":19916},{"git_mode":"100644","path":"Trainers/sft/src/model_loader.py","sha256":"00322b6e586eb0245675d1a5245172409ec477bbaccf33bf54af8534d9252d2b","size_bytes":19824},{"git_mode":"100644","path":"Trainers/sft/src/preprocessing.py","sha256":"db52901528b4a48d0a3ae3afbf2ec664b5e957ed467161a8535b0d953588a40e","size_bytes":15145},{"git_mode":"100644","path":"Trainers/sft/src/training_callbacks.py","sha256":"d04ed8c3d95361a2b73443de16519dc9540c96ee4276f80b5d109eaf13cd7b98","size_bytes":4201},{"git_mode":"100644","path":"Trainers/sft/train_sft.py","sha256":"75c2ab22fbc5dd80b8ddd5e6d0da816d1451cda0cd620c34717336a39a8a7644","size_bytes":88561},{"git_mode":"100644","path":"Trainers/shared/__init__.py","sha256":"39bf91bda8d3a2a76f70275c67b66d28f2bdd0c53c4d38b96f20ef540e431b6d","size_bytes":217},{"git_mode":"100644","path":"Trainers/shared/callbacks/__init__.py","sha256":"a919bb7953721dd2e001cad1723c5a8ba9c4e77608505df1570fba8906a3ff33","size_bytes":1743},{"git_mode":"100644","path":"Trainers/shared/callbacks/base.py","sha256":"4c8225571ae38b321dce1898f44fba62393d95e5adbcf072ad69007e937ec680","size_bytes":25232},{"git_mode":"100644","path":"Trainers/shared/callbacks/checkpoints.py","sha256":"3b86700a42dc0cc2d37a9fbebe5c5f97fc468bed310eecb6c5d76f3d2aff0080","size_bytes":561},{"git_mode":"100644","path":"Trainers/shared/callbacks/health_checks.py","sha256":"9f3732cee2173ada04a19c1e626100ca6ed1a50c12cb9809f48cc7cc004ee14f","size_bytes":3388},{"git_mode":"100644","path":"Trainers/shared/callbacks/log_suppression.py","sha256":"b30cc981f9f3c4f2046afdbf294ac848914f3757743f4d5d11a3280296aecaa2","size_bytes":787},{"git_mode":"100644","path":"Trainers/shared/callbacks/lr_schedules.py","sha256":"8e66ce42956fd7eedaa8dbe0a91feeb26c39df0cb0faf4871a1022a0aa7b6623","size_bytes":1938},{"git_mode":"100644","path":"schemas/synaptic-execution-source-v1.schema.json","sha256":"fa80f663b64b4ffdfb2e54c0a321cf7374ebeeb850b56b7a2fa00fd51ff81455","size_bytes":6227},{"git_mode":"100644","path":"schemas/synaptic-sft-workload-v1.schema.json","sha256":"d6d0c756619fc58f10041ff73e205cab99a14435e17379d950757dfbde4d698e","size_bytes":6651},{"git_mode":"100644","path":"shared/cloud_artifacts.py","sha256":"c7f95ef077b0bb5102ac66b6e4fd80b07470edec5e3fefd0d8fffaa6238eca4a","size_bytes":16737},{"git_mode":"100644","path":"shared/env_bootstrap.py","sha256":"4a8d3dd248639ea96fb8f94e5587b190145e49f75d2ebe2a01f05e4e1521c85c","size_bytes":4344},{"git_mode":"100644","path":"shared/experiment_tracking/__init__.py","sha256":"96e521ab64e8b7697d3aa7368346db7b8215997ffe4604cadaa8b022e39a5db2","size_bytes":2305},{"git_mode":"100644","path":"shared/experiment_tracking/experiment.py","sha256":"eb569f16b8b37f63ed6ba0ee7eaa76daf6940923c8c0c5a63b84a659419dae00","size_bytes":18628},{"git_mode":"100644","path":"shared/experiment_tracking/experiment_spec.py","sha256":"2ab5aaf808b49bdccc99cd857394ac4898e5242af33023a9855c702181394da3","size_bytes":11670},{"git_mode":"100644","path":"shared/experiment_tracking/lineage_enrichment.py","sha256":"accd4bb5b46af302b983c4c49b0f13976841d8b12672149feb98f99886fc3be5","size_bytes":9075},{"git_mode":"100644","path":"shared/experiment_tracking/schema.py","sha256":"871a00a4d4c90030a29be2d5c94669bed244921f00898c750423c29a92e02465","size_bytes":18789},{"git_mode":"100644","path":"shared/sft_preprocessing.py","sha256":"3cbac1e9b3d75fe6d7f1d6cb578d8c8b05de669249526a6f92a41484e4cb42b2","size_bytes":33273},{"git_mode":"100644","path":"shared/training_capacity.py","sha256":"2fae8c83e423a66b0ec56abc0e415363d790c12329120aac4c2103c298ad0e48","size_bytes":19299},{"git_mode":"100644","path":"shared/training_utils.py","sha256":"15136abdb987a7bdfbc8917c49f9c77ff553e91ce53890e239ea629241345a65","size_bytes":28814},{"git_mode":"100644","path":"shared/utilities/__init__.py","sha256":"546349addf2ba6c72661e185477354d743f6a42e8ef3bf8e78ed19a17b85e2ab","size_bytes":297},{"git_mode":"100644","path":"shared/utilities/env.py","sha256":"06f56639bc506d4e497ee79d4393d17d499bfd561fd0d0679c0ea7e103b8e886","size_bytes":3318},{"git_mode":"100644","path":"shared/utilities/paths.py","sha256":"3bfa1a13e4deef02cd91a920410e839f8ab0d34ec1081903cbcb742589e995b4","size_bytes":6843},{"git_mode":"100644","path":"shared/utilities/unique_ids.py","sha256":"4f5fd65acc46fc7458a79047403a3f50d5a92c96ea6efcd7ffc560aa329e4fae","size_bytes":678},{"git_mode":"100644","path":"shared/utilities/yaml_loader.py","sha256":"203996ff297e1714b0a3e40446f719e24b6d0e20eddb91bc528100b497d8591c","size_bytes":679},{"git_mode":"100644","path":"synaptic_tuner/__init__.py","sha256":"2462ff4ec439b77eb1ecfa901c1d8294628c853e9bbdf872de73dce9bfc0ae4d","size_bytes":134},{"git_mode":"100644","path":"synaptic_tuner/_version.py","sha256":"49fa86b62849d537e7e131445f272650ed77760db5f218f2113155ab60caf644","size_bytes":96},{"git_mode":"100644","path":"synaptic_tuner/api/__init__.py","sha256":"25c68ed273bf1e6b158021b98f8607276d980540a98b56a07801dccf9d6596a8","size_bytes":67},{"git_mode":"100644","path":"synaptic_tuner/api/v1/__init__.py","sha256":"4bc30420b18b3932183dcd0485892fa22f4f8e474ca3bb9b5e08adf3ae0fde3d","size_bytes":16893},{"git_mode":"100644","path":"synaptic_tuner/api/v1/_contract.py","sha256":"dcd6fc1ebdc75fbfd74ad4c6c96837b73209081258353557fedaaca76164673a","size_bytes":6702},{"git_mode":"100644","path":"synaptic_tuner/api/v1/_timestamps.py","sha256":"2b2ad7183f62bf1d42c4b9144d0315fe90c0e601649fb37f7778d017c632935d","size_bytes":698},{"git_mode":"100644","path":"synaptic_tuner/api/v1/execution.py","sha256":"3309cbfdea5a7746b0463147a22bb61d8e14028134ef5a2b05dadcad985fdfbc","size_bytes":5506},{"git_mode":"100644","path":"synaptic_tuner/api/v1/observations.py","sha256":"b33f5826e6c03db889e0ff29c3eba65f5eab436b3d3f5146b8e4317828178d07","size_bytes":26569},{"git_mode":"100644","path":"synaptic_tuner/api/v1/sources.py","sha256":"65e410cdfe331760bbb03626cd58284d0a5f946b4cc081ba457ad639f4c1e5ef","size_bytes":858},{"git_mode":"100644","path":"tuner/__init__.py","sha256":"920cf8a0ca993a712fbacf734621e1fdf14fdd2a7203701db120dc41e4815e7b","size_bytes":2666},{"git_mode":"100644","path":"tuner/cloud/__init__.py","sha256":"a9abb71aa9222c511524d3cc49b1cfe00f8938a6cb7c61792cf6135a6b82208b","size_bytes":3692},{"git_mode":"100644","path":"tuner/cloud/hardware_planner.py","sha256":"62467f775f5201229e5dc0f34ed72caed760724466dcf990a9c3fb9e7ca04c5d","size_bytes":25209},{"git_mode":"100644","path":"tuner/execution/__init__.py","sha256":"8193dfcd30c68a7d8ad02af0fb24f1a8120fce2a709decd132ab1acb33113d65","size_bytes":535},{"git_mode":"100644","path":"tuner/execution/contracts.py","sha256":"82d90f742e4e33d8071e557b5a872e0158684d94db5debdfbb85411954e4e638","size_bytes":32907},{"git_mode":"100644","path":"tuner/execution/evidence.py","sha256":"a04cc6843a794e62a702608d28c8e82d0c57af8227b3cb9743cf56b9605442ca","size_bytes":4634},{"git_mode":"100644","path":"tuner/execution/lifecycle.py","sha256":"4bc878038fc2384c0213a5f5c887aca8b15a67ed46b11e1acae1e2ecf0e97793","size_bytes":17660},{"git_mode":"100644","path":"tuner/execution/registry.py","sha256":"0cf424ff4b6bd628bae86b347f00a10acce8793cc41afc2ff1f3abbb738aec40","size_bytes":1515},{"git_mode":"100644","path":"tuner/execution/service.py","sha256":"a85e31e8a54479764463633a9c721b9af3afe38710f85efa35b9a5744ad33e4c","size_bytes":4514},{"git_mode":"100644","path":"tuner/project/__init__.py","sha256":"a858562f45452031f8a0e8ba4c9799f9784e09360a0258165dc3c4f89a46ca23","size_bytes":1513},{"git_mode":"100644","path":"tuner/project/config_layers.py","sha256":"0e06ddfb8b92faa106d052990df34f199c06c298c3a3a2366cab218f2c1731f1","size_bytes":6714},{"git_mode":"100644","path":"tuner/project/context.py","sha256":"147d08d5d6fcb3cf258ab347f070424e65e5c0c28e47ad1fd062e13cf56a8dd3","size_bytes":6518},{"git_mode":"100644","path":"tuner/project/errors.py","sha256":"6f1f94cd0b4e068a0cdee82259de0aee09353345e93caeb05272097a5e717444","size_bytes":1511},{"git_mode":"100644","path":"tuner/project/execution_source.py","sha256":"dc196dff10029c9a77b6d94fbfbcca45b39da4840bff40009b7152fd39d9622b","size_bytes":30595},{"git_mode":"100644","path":"tuner/project/git_verification.py","sha256":"0cfe00560c704eddbe688a0c7156c12f9acd2f321046017f50eb5d9e3bb61710","size_bytes":9767},{"git_mode":"100644","path":"tuner/project/manifest.py","sha256":"5506cf2ffb261988c0bbc5ffab1e466236bd10f503df5e805453f5ddf7d34be0","size_bytes":9498},{"git_mode":"100644","path":"tuner/project/path_refs.py","sha256":"35e47de6ed1c6096ba45e7ea64aedcc0f79aeb9425acb5041b14afb9d258c955","size_bytes":6287},{"git_mode":"100644","path":"tuner/project/secrets.py","sha256":"6f267060b0f24ab1517c4d4499fe54a993a77c2c78830c2d79415da871893818","size_bytes":4256},{"git_mode":"100644","path":"tuner/project/source_bundle.py","sha256":"16368658db727680f74326603e2d28f3ae9b5e481f7626c7be1a8280b588ecec","size_bytes":28339},{"git_mode":"100644","path":"tuner/runtime/__init__.py","sha256":"246429ce58a07dabeb05bf17967d3a7862014d114ee995a7419adcb27227286c","size_bytes":3566},{"git_mode":"100644","path":"tuner/runtime/artifacts.py","sha256":"c9eaefd6f6805af11d997256e14392c0c42173b852d94bfab90bcb20a1ae2cd2","size_bytes":6913},{"git_mode":"100644","path":"tuner/runtime/offline_sft_worker.py","sha256":"248f258254bb9c7021eed2ee0ff32d9633a021e656c10efb15825e7f6aaa9366","size_bytes":32592},{"git_mode":"100644","path":"tuner/training/__init__.py","sha256":"23ca59d6203276c6160e0bb57dbd5c43cf385c6aec60b4e3e08b93d1d501cb97","size_bytes":2995},{"git_mode":"100644","path":"tuner/training/contracts.py","sha256":"91c4d66d9810cc0471981c3d4ef4b54feaed5b92eb41a468f31201f8111303b5","size_bytes":21504},{"git_mode":"100644","path":"tuner/training/methods/__init__.py","sha256":"99db689246273707ae3df5087859ebb510e84b664f9f173c0d5827abc3e6a3ea","size_bytes":361},{"git_mode":"100644","path":"tuner/training/methods/sft.py","sha256":"b501f9c214dec4b0b1550543281c25aba8dbb102604df253c641c85f7cae587b","size_bytes":9997},{"git_mode":"100644","path":"tuner/training/recipes.py","sha256":"bb844ed4883f00ccea9936eebad50c466e6e4bc148e88aa680d93121edf67dcc","size_bytes":6300}],"optional_features":[],"owned_module_prefixes":["tuner","synaptic_tuner","Trainers","shared","SynthChat","Evaluator","MechInterp","configs","src"],"payload_bytes":838264,"schema_version":"synaptic-offline-sft-worker-closure/v1","trainer_entrypoint":"Trainers/sft/train_sft.py"} +{"closure_digest":"8465ff2b7cedb352337e7cedc5bb17cc169892c2eedae0b280569f4a0cde5002","closure_ref":"synaptic/offline-sft-worker/v1","entrypoint":"Trainers/sft/runtime_v1.py","member_count":68,"members":[{"git_mode":"100644","path":"Trainers/sft/configs/config.yaml","sha256":"93b85493fa4200a103267ca7ba1223dcd519be3d0cf9017304ebcdc2e7a99ec1","size_bytes":5422},{"git_mode":"100644","path":"Trainers/sft/configs/config_loader.py","sha256":"4f90bf152d191544f9a567e22f41a171aa199e8995a6a7ea00a7098bc44a4060","size_bytes":19126},{"git_mode":"100644","path":"Trainers/sft/runtime_v1.py","sha256":"460121aaee3eb83764f67d9307cfb57a3b25c117611d987577b9631b6956a1b3","size_bytes":104495},{"git_mode":"100644","path":"Trainers/sft/src/data_loader.py","sha256":"34d664535b4d26f9a6e9b1a4121e37b575f3960ea39b6fd651f2fe9b624ffb09","size_bytes":19916},{"git_mode":"100644","path":"Trainers/sft/src/model_loader.py","sha256":"00322b6e586eb0245675d1a5245172409ec477bbaccf33bf54af8534d9252d2b","size_bytes":19824},{"git_mode":"100644","path":"Trainers/sft/src/preprocessing.py","sha256":"db52901528b4a48d0a3ae3afbf2ec664b5e957ed467161a8535b0d953588a40e","size_bytes":15145},{"git_mode":"100644","path":"Trainers/sft/src/training_callbacks.py","sha256":"d04ed8c3d95361a2b73443de16519dc9540c96ee4276f80b5d109eaf13cd7b98","size_bytes":4201},{"git_mode":"100644","path":"Trainers/sft/train_sft.py","sha256":"75c2ab22fbc5dd80b8ddd5e6d0da816d1451cda0cd620c34717336a39a8a7644","size_bytes":88561},{"git_mode":"100644","path":"Trainers/shared/__init__.py","sha256":"39bf91bda8d3a2a76f70275c67b66d28f2bdd0c53c4d38b96f20ef540e431b6d","size_bytes":217},{"git_mode":"100644","path":"Trainers/shared/callbacks/__init__.py","sha256":"a919bb7953721dd2e001cad1723c5a8ba9c4e77608505df1570fba8906a3ff33","size_bytes":1743},{"git_mode":"100644","path":"Trainers/shared/callbacks/base.py","sha256":"4c8225571ae38b321dce1898f44fba62393d95e5adbcf072ad69007e937ec680","size_bytes":25232},{"git_mode":"100644","path":"Trainers/shared/callbacks/checkpoints.py","sha256":"3b86700a42dc0cc2d37a9fbebe5c5f97fc468bed310eecb6c5d76f3d2aff0080","size_bytes":561},{"git_mode":"100644","path":"Trainers/shared/callbacks/health_checks.py","sha256":"9f3732cee2173ada04a19c1e626100ca6ed1a50c12cb9809f48cc7cc004ee14f","size_bytes":3388},{"git_mode":"100644","path":"Trainers/shared/callbacks/log_suppression.py","sha256":"b30cc981f9f3c4f2046afdbf294ac848914f3757743f4d5d11a3280296aecaa2","size_bytes":787},{"git_mode":"100644","path":"Trainers/shared/callbacks/lr_schedules.py","sha256":"8e66ce42956fd7eedaa8dbe0a91feeb26c39df0cb0faf4871a1022a0aa7b6623","size_bytes":1938},{"git_mode":"100644","path":"schemas/synaptic-execution-source-v1.schema.json","sha256":"fa80f663b64b4ffdfb2e54c0a321cf7374ebeeb850b56b7a2fa00fd51ff81455","size_bytes":6227},{"git_mode":"100644","path":"schemas/synaptic-sft-workload-v1.schema.json","sha256":"d6d0c756619fc58f10041ff73e205cab99a14435e17379d950757dfbde4d698e","size_bytes":6651},{"git_mode":"100644","path":"shared/cloud_artifacts.py","sha256":"c7f95ef077b0bb5102ac66b6e4fd80b07470edec5e3fefd0d8fffaa6238eca4a","size_bytes":16737},{"git_mode":"100644","path":"shared/env_bootstrap.py","sha256":"4a8d3dd248639ea96fb8f94e5587b190145e49f75d2ebe2a01f05e4e1521c85c","size_bytes":4344},{"git_mode":"100644","path":"shared/experiment_tracking/__init__.py","sha256":"96e521ab64e8b7697d3aa7368346db7b8215997ffe4604cadaa8b022e39a5db2","size_bytes":2305},{"git_mode":"100644","path":"shared/experiment_tracking/experiment.py","sha256":"eb569f16b8b37f63ed6ba0ee7eaa76daf6940923c8c0c5a63b84a659419dae00","size_bytes":18628},{"git_mode":"100644","path":"shared/experiment_tracking/experiment_spec.py","sha256":"2ab5aaf808b49bdccc99cd857394ac4898e5242af33023a9855c702181394da3","size_bytes":11670},{"git_mode":"100644","path":"shared/experiment_tracking/lineage_enrichment.py","sha256":"accd4bb5b46af302b983c4c49b0f13976841d8b12672149feb98f99886fc3be5","size_bytes":9075},{"git_mode":"100644","path":"shared/experiment_tracking/schema.py","sha256":"871a00a4d4c90030a29be2d5c94669bed244921f00898c750423c29a92e02465","size_bytes":18789},{"git_mode":"100644","path":"shared/sft_preprocessing.py","sha256":"3cbac1e9b3d75fe6d7f1d6cb578d8c8b05de669249526a6f92a41484e4cb42b2","size_bytes":33273},{"git_mode":"100644","path":"shared/training_capacity.py","sha256":"2fae8c83e423a66b0ec56abc0e415363d790c12329120aac4c2103c298ad0e48","size_bytes":19299},{"git_mode":"100644","path":"shared/training_utils.py","sha256":"15136abdb987a7bdfbc8917c49f9c77ff553e91ce53890e239ea629241345a65","size_bytes":28814},{"git_mode":"100644","path":"shared/utilities/__init__.py","sha256":"546349addf2ba6c72661e185477354d743f6a42e8ef3bf8e78ed19a17b85e2ab","size_bytes":297},{"git_mode":"100644","path":"shared/utilities/env.py","sha256":"06f56639bc506d4e497ee79d4393d17d499bfd561fd0d0679c0ea7e103b8e886","size_bytes":3318},{"git_mode":"100644","path":"shared/utilities/paths.py","sha256":"3bfa1a13e4deef02cd91a920410e839f8ab0d34ec1081903cbcb742589e995b4","size_bytes":6843},{"git_mode":"100644","path":"shared/utilities/unique_ids.py","sha256":"4f5fd65acc46fc7458a79047403a3f50d5a92c96ea6efcd7ffc560aa329e4fae","size_bytes":678},{"git_mode":"100644","path":"shared/utilities/yaml_loader.py","sha256":"203996ff297e1714b0a3e40446f719e24b6d0e20eddb91bc528100b497d8591c","size_bytes":679},{"git_mode":"100644","path":"synaptic_tuner/__init__.py","sha256":"2462ff4ec439b77eb1ecfa901c1d8294628c853e9bbdf872de73dce9bfc0ae4d","size_bytes":134},{"git_mode":"100644","path":"synaptic_tuner/_version.py","sha256":"49fa86b62849d537e7e131445f272650ed77760db5f218f2113155ab60caf644","size_bytes":96},{"git_mode":"100644","path":"synaptic_tuner/api/__init__.py","sha256":"25c68ed273bf1e6b158021b98f8607276d980540a98b56a07801dccf9d6596a8","size_bytes":67},{"git_mode":"100644","path":"synaptic_tuner/api/v1/__init__.py","sha256":"4bc30420b18b3932183dcd0485892fa22f4f8e474ca3bb9b5e08adf3ae0fde3d","size_bytes":16893},{"git_mode":"100644","path":"synaptic_tuner/api/v1/_contract.py","sha256":"dcd6fc1ebdc75fbfd74ad4c6c96837b73209081258353557fedaaca76164673a","size_bytes":6702},{"git_mode":"100644","path":"synaptic_tuner/api/v1/_timestamps.py","sha256":"2b2ad7183f62bf1d42c4b9144d0315fe90c0e601649fb37f7778d017c632935d","size_bytes":698},{"git_mode":"100644","path":"synaptic_tuner/api/v1/execution.py","sha256":"3309cbfdea5a7746b0463147a22bb61d8e14028134ef5a2b05dadcad985fdfbc","size_bytes":5506},{"git_mode":"100644","path":"synaptic_tuner/api/v1/observations.py","sha256":"b33f5826e6c03db889e0ff29c3eba65f5eab436b3d3f5146b8e4317828178d07","size_bytes":26569},{"git_mode":"100644","path":"synaptic_tuner/api/v1/sources.py","sha256":"65e410cdfe331760bbb03626cd58284d0a5f946b4cc081ba457ad639f4c1e5ef","size_bytes":858},{"git_mode":"100644","path":"tuner/__init__.py","sha256":"920cf8a0ca993a712fbacf734621e1fdf14fdd2a7203701db120dc41e4815e7b","size_bytes":2666},{"git_mode":"100644","path":"tuner/cloud/__init__.py","sha256":"a9abb71aa9222c511524d3cc49b1cfe00f8938a6cb7c61792cf6135a6b82208b","size_bytes":3692},{"git_mode":"100644","path":"tuner/cloud/hardware_planner.py","sha256":"62467f775f5201229e5dc0f34ed72caed760724466dcf990a9c3fb9e7ca04c5d","size_bytes":25209},{"git_mode":"100644","path":"tuner/execution/__init__.py","sha256":"8193dfcd30c68a7d8ad02af0fb24f1a8120fce2a709decd132ab1acb33113d65","size_bytes":535},{"git_mode":"100644","path":"tuner/execution/contracts.py","sha256":"82d90f742e4e33d8071e557b5a872e0158684d94db5debdfbb85411954e4e638","size_bytes":32907},{"git_mode":"100644","path":"tuner/execution/evidence.py","sha256":"a04cc6843a794e62a702608d28c8e82d0c57af8227b3cb9743cf56b9605442ca","size_bytes":4634},{"git_mode":"100644","path":"tuner/execution/lifecycle.py","sha256":"4bc878038fc2384c0213a5f5c887aca8b15a67ed46b11e1acae1e2ecf0e97793","size_bytes":17660},{"git_mode":"100644","path":"tuner/execution/registry.py","sha256":"0cf424ff4b6bd628bae86b347f00a10acce8793cc41afc2ff1f3abbb738aec40","size_bytes":1515},{"git_mode":"100644","path":"tuner/execution/service.py","sha256":"a85e31e8a54479764463633a9c721b9af3afe38710f85efa35b9a5744ad33e4c","size_bytes":4514},{"git_mode":"100644","path":"tuner/project/__init__.py","sha256":"a858562f45452031f8a0e8ba4c9799f9784e09360a0258165dc3c4f89a46ca23","size_bytes":1513},{"git_mode":"100644","path":"tuner/project/config_layers.py","sha256":"0e06ddfb8b92faa106d052990df34f199c06c298c3a3a2366cab218f2c1731f1","size_bytes":6714},{"git_mode":"100644","path":"tuner/project/context.py","sha256":"147d08d5d6fcb3cf258ab347f070424e65e5c0c28e47ad1fd062e13cf56a8dd3","size_bytes":6518},{"git_mode":"100644","path":"tuner/project/errors.py","sha256":"6f1f94cd0b4e068a0cdee82259de0aee09353345e93caeb05272097a5e717444","size_bytes":1511},{"git_mode":"100644","path":"tuner/project/execution_source.py","sha256":"dc196dff10029c9a77b6d94fbfbcca45b39da4840bff40009b7152fd39d9622b","size_bytes":30595},{"git_mode":"100644","path":"tuner/project/git_verification.py","sha256":"0cfe00560c704eddbe688a0c7156c12f9acd2f321046017f50eb5d9e3bb61710","size_bytes":9767},{"git_mode":"100644","path":"tuner/project/manifest.py","sha256":"5506cf2ffb261988c0bbc5ffab1e466236bd10f503df5e805453f5ddf7d34be0","size_bytes":9498},{"git_mode":"100644","path":"tuner/project/path_refs.py","sha256":"35e47de6ed1c6096ba45e7ea64aedcc0f79aeb9425acb5041b14afb9d258c955","size_bytes":6287},{"git_mode":"100644","path":"tuner/project/secrets.py","sha256":"6f267060b0f24ab1517c4d4499fe54a993a77c2c78830c2d79415da871893818","size_bytes":4256},{"git_mode":"100644","path":"tuner/project/source_bundle.py","sha256":"16368658db727680f74326603e2d28f3ae9b5e481f7626c7be1a8280b588ecec","size_bytes":28339},{"git_mode":"100644","path":"tuner/runtime/__init__.py","sha256":"246429ce58a07dabeb05bf17967d3a7862014d114ee995a7419adcb27227286c","size_bytes":3566},{"git_mode":"100644","path":"tuner/runtime/artifacts.py","sha256":"c9eaefd6f6805af11d997256e14392c0c42173b852d94bfab90bcb20a1ae2cd2","size_bytes":6913},{"git_mode":"100644","path":"tuner/runtime/offline_sft_worker.py","sha256":"2304eeb00667954510c34cc4aacaa818532a301fbeabf04c2fb165d6cd1d70b5","size_bytes":36343},{"git_mode":"100644","path":"tuner/training/__init__.py","sha256":"23ca59d6203276c6160e0bb57dbd5c43cf385c6aec60b4e3e08b93d1d501cb97","size_bytes":2995},{"git_mode":"100644","path":"tuner/training/contracts.py","sha256":"91c4d66d9810cc0471981c3d4ef4b54feaed5b92eb41a468f31201f8111303b5","size_bytes":21504},{"git_mode":"100644","path":"tuner/training/methods/__init__.py","sha256":"99db689246273707ae3df5087859ebb510e84b664f9f173c0d5827abc3e6a3ea","size_bytes":361},{"git_mode":"100644","path":"tuner/training/methods/sft.py","sha256":"b501f9c214dec4b0b1550543281c25aba8dbb102604df253c641c85f7cae587b","size_bytes":9997},{"git_mode":"100644","path":"tuner/training/recipes.py","sha256":"bb844ed4883f00ccea9936eebad50c466e6e4bc148e88aa680d93121edf67dcc","size_bytes":6300}],"optional_features":[],"owned_module_prefixes":["tuner","synaptic_tuner","Trainers","shared","SynthChat","Evaluator","MechInterp","configs","src"],"payload_bytes":842015,"schema_version":"synaptic-offline-sft-worker-closure/v1","trainer_entrypoint":"Trainers/sft/train_sft.py"} diff --git a/tuner/runtime/manifests/packaged-grpo-worker-v1.json b/tuner/runtime/manifests/packaged-grpo-worker-v1.json index dc092b8e..2f1dac12 100644 --- a/tuner/runtime/manifests/packaged-grpo-worker-v1.json +++ b/tuner/runtime/manifests/packaged-grpo-worker-v1.json @@ -1 +1 @@ -{"closure_digest":"139c0dfb97533902df8d784bd5ab9141a6c539aa46227a79461ca5c3b94f02e6","members":[{"path":"tuner/runtime/manifests/offline-grpo-worker-v1.json","sha256":"ee7029ba07536a0e0609917d967403dbee10b31b8d12174149aae1a8fbc01d39","size_bytes":21344},{"path":"tuner/runtime/offline_grpo_worker.py","sha256":"aac831048855e959598a4dc45b29e614f06f8714df18e868fd6b069e3c765710","size_bytes":9323},{"path":"tuner/runtime/offline_sft_worker.py","sha256":"248f258254bb9c7021eed2ee0ff32d9633a021e656c10efb15825e7f6aaa9366","size_bytes":32592},{"path":"tuner/runtime/packaged_grpo_child.py","sha256":"124d5b2a969d38ac2e488d58a216759108f1a55dfcc3dcaf1012791efe792e4b","size_bytes":8273},{"path":"tuner/runtime/packaged_grpo_execution.py","sha256":"a77c7d6b97ad2ded3b9631174e479e60462f1894513f90fd2fc7798828c6867d","size_bytes":14943},{"path":"tuner/runtime/packaged_grpo_verification.py","sha256":"56564c6ee6414a3a71ce7c9ef330152d812fede2a711a2329463a0b81a61b0d6","size_bytes":10319},{"path":"tuner/runtime/packaged_grpo_worker_closure.py","sha256":"81da8db5a61e9a874234d1e50c529e1056282fd0eef133b2720eb0f34f78cddc","size_bytes":3735},{"path":"tuner/runtime/packaged_sft_child.py","sha256":"ab41eb7dca28b440c043f589d297dc0d9f718ed14f6cb36c96d7bd1b7bfcdc14","size_bytes":23556},{"path":"tuner/runtime/packaged_sft_execution.py","sha256":"cab2b5032d16146422398305120f52c5a124287ecf929b640a1f17d0ea2ed648","size_bytes":80053},{"path":"tuner/runtime/packaged_training_worker.py","sha256":"9cfe13dc9f0124b4da01d05e92cf3c2c9249d075c3a508d6b93b0a7c7f0e109f","size_bytes":34986},{"path":"tuner/runtime/packaged_worker_closure.py","sha256":"e457937426e19747e867a314c1a2556ee64bd5cbc19359f535253b02403d1443","size_bytes":8705},{"path":"tuner/runtime/releases.py","sha256":"c0aa7ba37e8d35287fdd480cf0c76053a554e546c40c8bf36f5cf05d1a96d22b","size_bytes":52535},{"path":"tuner/training/packaged_grpo_compilation.py","sha256":"d186a05ffb8cc756e782b2bb9a3f66e925779104b70045bd56a5592846fec524","size_bytes":8722}],"schema_version":"synaptic-packaged-grpo-worker-closure/v1"} +{"closure_digest":"e48763871e7e9bc50c0c2b218bbfcb885b72b56f2ae1c2c41b1e16938c198ffe","members":[{"path":"tuner/runtime/manifests/offline-grpo-worker-v1.json","sha256":"7159247a2d15faaf2654b14168c1f90359b09950f646d0e9b2e2ec683f20617b","size_bytes":21344},{"path":"tuner/runtime/offline_grpo_worker.py","sha256":"aac831048855e959598a4dc45b29e614f06f8714df18e868fd6b069e3c765710","size_bytes":9323},{"path":"tuner/runtime/offline_sft_worker.py","sha256":"2304eeb00667954510c34cc4aacaa818532a301fbeabf04c2fb165d6cd1d70b5","size_bytes":36343},{"path":"tuner/runtime/packaged_grpo_child.py","sha256":"124d5b2a969d38ac2e488d58a216759108f1a55dfcc3dcaf1012791efe792e4b","size_bytes":8273},{"path":"tuner/runtime/packaged_grpo_execution.py","sha256":"a77c7d6b97ad2ded3b9631174e479e60462f1894513f90fd2fc7798828c6867d","size_bytes":14943},{"path":"tuner/runtime/packaged_grpo_verification.py","sha256":"56564c6ee6414a3a71ce7c9ef330152d812fede2a711a2329463a0b81a61b0d6","size_bytes":10319},{"path":"tuner/runtime/packaged_grpo_worker_closure.py","sha256":"81da8db5a61e9a874234d1e50c529e1056282fd0eef133b2720eb0f34f78cddc","size_bytes":3735},{"path":"tuner/runtime/packaged_sft_child.py","sha256":"ab41eb7dca28b440c043f589d297dc0d9f718ed14f6cb36c96d7bd1b7bfcdc14","size_bytes":23556},{"path":"tuner/runtime/packaged_sft_execution.py","sha256":"cab2b5032d16146422398305120f52c5a124287ecf929b640a1f17d0ea2ed648","size_bytes":80053},{"path":"tuner/runtime/packaged_training_worker.py","sha256":"9cfe13dc9f0124b4da01d05e92cf3c2c9249d075c3a508d6b93b0a7c7f0e109f","size_bytes":34986},{"path":"tuner/runtime/packaged_worker_closure.py","sha256":"e457937426e19747e867a314c1a2556ee64bd5cbc19359f535253b02403d1443","size_bytes":8705},{"path":"tuner/runtime/releases.py","sha256":"c0aa7ba37e8d35287fdd480cf0c76053a554e546c40c8bf36f5cf05d1a96d22b","size_bytes":52535},{"path":"tuner/training/packaged_grpo_compilation.py","sha256":"d186a05ffb8cc756e782b2bb9a3f66e925779104b70045bd56a5592846fec524","size_bytes":8722}],"schema_version":"synaptic-packaged-grpo-worker-closure/v1"} diff --git a/tuner/runtime/native_mlx_runtime.py b/tuner/runtime/native_mlx_runtime.py new file mode 100644 index 00000000..21bbe75c --- /dev/null +++ b/tuner/runtime/native_mlx_runtime.py @@ -0,0 +1,126 @@ +"""Read-only native candidate comparison, not a runtime attestation or installer. + +The host supplies reviewed exact pins derived from the retained wheel install +and interpreter capture. This compares metadata and interpreter bytes before +worker ML imports. Native libraries, loaded-module origins and Metal behavior +still require independent qualification; no RuntimeSpec is admitted here. +""" + +from __future__ import annotations + +from dataclasses import dataclass +import hashlib +from importlib import metadata +import os +from pathlib import Path +import platform +import re +import sys + + +_NAME = re.compile(r"[a-z0-9]+(?:-[a-z0-9]+)*") +_VERSION = re.compile(r"[A-Za-z0-9][A-Za-z0-9.+!_-]{0,127}") +_DIGEST = re.compile(r"[0-9a-f]{64}") + + +@dataclass(frozen=True) +class MLXRuntimeExpectation: + """Host-owned candidate pins. Equality is necessary, not execution authority.""" + + python_version: str + executable_sha256: str + dependency_lock_sha256: str + distributions: tuple[tuple[str, str], ...] + + def __post_init__(self) -> None: + if type(self.python_version) is not str or not re.fullmatch(r"3\.\d+\.\d+", self.python_version): + raise ValueError("native candidate Python pin is invalid") + for value in (self.executable_sha256, self.dependency_lock_sha256): + if type(value) is not str or not _DIGEST.fullmatch(value): + raise ValueError("native candidate digest pin is invalid") + if type(self.distributions) is not tuple or not 2 <= len(self.distributions) <= 256: + raise ValueError("native candidate inventory is invalid") + seen = set() + for item in self.distributions: + if type(item) is not tuple or len(item) != 2: + raise ValueError("native candidate distribution pin is invalid") + name, version = item + if ( + type(name) is not str or not _NAME.fullmatch(name) or name in seen + or type(version) is not str or not _VERSION.fullmatch(version) + ): + raise ValueError("native candidate distribution pin is invalid") + seen.add(name) + if not {"mlx", "mlx-lm"} <= seen or tuple(sorted(self.distributions)) != self.distributions: + raise ValueError("native candidate inventory is incomplete or unordered") + + @classmethod + def from_dict(cls, value: dict[str, object]) -> MLXRuntimeExpectation: + if type(value) is not dict or set(value) != set(cls.__dataclass_fields__): + raise ValueError("native candidate has missing or unknown fields") + distributions = value["distributions"] + if type(distributions) is not dict: + raise ValueError("native candidate inventory must be an object") + return cls( + value["python_version"], value["executable_sha256"], + value["dependency_lock_sha256"], tuple(sorted(distributions.items())), + ) + + +def inspect_mlx_runtime_candidate( + expected: MLXRuntimeExpectation, *, dependency_lock: bytes, +) -> dict[str, object]: + """Compare complete observed package versions and the running executable. + + No install, framework import, model access, environment dump, or GPU call. + The lock is opaque reviewed bytes; this is not a dependency resolver or a + proof that installed package bytes match the allowed wheel hashes. + """ + if type(expected) is not MLXRuntimeExpectation: + raise TypeError("native candidate requires exact configured expectations") + if ( + type(dependency_lock) is not bytes or not 1 <= len(dependency_lock) <= 1048576 + or hashlib.sha256(dependency_lock).hexdigest() != expected.dependency_lock_sha256 + ): + raise ValueError("native candidate dependency lock differs") + if ( + platform.system() != "Darwin" or platform.machine() != "arm64" + or sys.implementation.name != "cpython" + or platform.python_version() != expected.python_version + ): + raise ValueError("native candidate platform or Python differs") + executable = Path(sys.executable).resolve(strict=True) + with executable.open("rb") as stream: + before = os.fstat(stream.fileno()) + raw = stream.read(64 * 1024 * 1024 + 1) + after = os.fstat(stream.fileno()) + current = executable.stat() + if ( + len(raw) > 64 * 1024 * 1024 or len(raw) != before.st_size + or (before.st_dev, before.st_ino, before.st_size, before.st_mtime_ns) + != (after.st_dev, after.st_ino, after.st_size, after.st_mtime_ns) + or (after.st_dev, after.st_ino) != (current.st_dev, current.st_ino) + or hashlib.sha256(raw).hexdigest() != expected.executable_sha256 + ): + raise ValueError("native candidate interpreter bytes differ") + observed = {} + for index, distribution in enumerate(metadata.distributions()): + if index >= 256: + raise ValueError("native candidate distribution count exceeded") + name = distribution.metadata["Name"] + version = distribution.version + if type(name) is not str or len(name) > 128 or type(version) is not str: + raise ValueError("native candidate distribution metadata is invalid") + name = re.sub(r"[-_.]+", "-", name).lower() + if not _NAME.fullmatch(name) or not _VERSION.fullmatch(version) or name in observed: + raise ValueError("native candidate distribution metadata is ambiguous") + observed[name] = version + if tuple(sorted(observed.items())) != expected.distributions: + raise ValueError("native candidate installed inventory differs") + return { + "status": "CANDIDATE_MATCH_ONLY", + "python_version": expected.python_version, + "executable_sha256": expected.executable_sha256, + "dependency_lock_sha256": expected.dependency_lock_sha256, + "distributions": observed, + } diff --git a/tuner/runtime/native_mlx_sft.py b/tuner/runtime/native_mlx_sft.py new file mode 100644 index 00000000..1429af5b --- /dev/null +++ b/tuner/runtime/native_mlx_sft.py @@ -0,0 +1,302 @@ +"""Internal prepared-token SFT boundary for the candidate MLX runtime. + +This module is deliberately not a provider or launcher. The host must authenticate +the prepared input, tokenizer, runtime and model, retain the attempt, and execute +the worker in an owned bounded child. Importing/preparing here loads no ML library. +""" + +from __future__ import annotations + +from dataclasses import asdict, dataclass +import hashlib +import json +import random +from typing import Iterator + +from shared.sft_preprocessing import PreparedSFTExample +from synaptic_tuner.api.v1.training_sources import PreparedTrainingInputIdentity +from tuner.runtime.native_mlx_runtime import MLXRuntimeExpectation, inspect_mlx_runtime_candidate + + +def _integer(value: object, name: str, minimum: int, maximum: int) -> int: + if type(value) is not int or not minimum <= value <= maximum: + raise ValueError(f"native SFT {name} is invalid") + return value + + +def _text(value: object, name: str) -> str: + if type(value) is not str or not 1 <= len(value.encode("utf-8")) <= 512: + raise ValueError(f"native SFT {name} is invalid") + if any(ord(char) < 32 for char in value): + raise ValueError(f"native SFT {name} is invalid") + return value + + +@dataclass(frozen=True) +class MLXSFTConfig: + """Explicit worker controls; optimizer/model/LoRA preparation stays external. + + One batch is one update. Accumulation is intentionally unavailable: upstream + averages microbatch means, which differs from a token-weighted accumulated + objective for unequal target lengths. No implicit schedule or epoch count. + """ + + batch_size: int + max_seq_length: int + max_batch_tokens: int + iterations: int + seed: int + steps_per_report: int + steps_per_eval: int + steps_per_save: int + + def __post_init__(self) -> None: + for name, maximum in ( + ("batch_size", 1024), ("max_seq_length", 1048576), + ("max_batch_tokens", 1048576), ("iterations", 1000000), + ("steps_per_report", 1000000), ("steps_per_eval", 1000000), + ("steps_per_save", 1000000), + ): + _integer(getattr(self, name), name, 1, maximum) + _integer(self.seed, "seed", 0, 2**32 - 1) + if self.batch_size * self.max_seq_length > self.max_batch_tokens: + raise ValueError("native SFT batch exceeds the configured token bound") + + @classmethod + def from_dict(cls, value: dict[str, object]) -> MLXSFTConfig: + if type(value) is not dict or set(value) != set(cls.__dataclass_fields__): + raise ValueError("native SFT controls have missing or unknown fields") + return cls(**value) + + +@dataclass(frozen=True) +class _Row: + row_id: str + input_ids: tuple[int, ...] + labels: tuple[int, ...] + + +@dataclass(frozen=True) +class PreparedMLXSFT: + """Immutable projection of host-verified rows, not a new dataset publication. + + The binding records the host's provenance; it does not authenticate that the + caller actually obtained these examples from that source/tokenizer. + """ + + source: PreparedTrainingInputIdentity + tokenizer_revision: str + config: MLXSFTConfig + train: tuple[_Row, ...] + validation: tuple[_Row, ...] + + def canonical_bytes(self) -> bytes: + return json.dumps( + { + "source": self.source.to_dict(), + "tokenizer_revision": self.tokenizer_revision, + "config": asdict(self.config), + "train": [asdict(row) for row in self.train], + "validation": [asdict(row) for row in self.validation], + }, + sort_keys=True, separators=(",", ":"), ensure_ascii=False, + allow_nan=False, + ).encode("utf-8") + + @property + def digest(self) -> str: + return hashlib.sha256(self.canonical_bytes()).hexdigest() + + +def prepare_mlx_sft( + *, + source: PreparedTrainingInputIdentity, + tokenizer_revision: str, + train: tuple[tuple[str, PreparedSFTExample], ...], + validation: tuple[tuple[str, PreparedSFTExample], ...], + config: MLXSFTConfig, +) -> PreparedMLXSFT: + """Copy the shared preprocessor's exact tokens and masks without rendering. + + Splits and row IDs come from the host's retained prepared publication. No + tokenizer, EOS insertion, text loader, split generation, filtering, packing, + or truncation runs here. Unsupported attention masks fail rather than being + discarded: this first causal-model boundary supports unpadded rows only. + """ + if type(source) is not PreparedTrainingInputIdentity or type(config) is not MLXSFTConfig: + raise TypeError("native SFT requires exact source identity and controls") + _text(tokenizer_revision, "tokenizer revision") + seen: set[str] = set() + total_tokens = 0 + + def split(rows: tuple[tuple[str, PreparedSFTExample], ...]) -> tuple[_Row, ...]: + nonlocal total_tokens + if type(rows) is not tuple or not 1 <= len(rows) <= 100000: + raise ValueError("native SFT requires nonempty bounded declared splits") + result = [] + for item in rows: + if type(item) is not tuple or len(item) != 2: + raise TypeError("native SFT row must pair its retained ID with an example") + row_id, example = item + _text(row_id, "row ID") + if row_id in seen: + raise ValueError("native SFT row ID occurs more than once") + seen.add(row_id) + if type(example) is not PreparedSFTExample: + raise TypeError("native SFT requires shared prepared examples") + if ( + example.truncation_applied is not False + or example.drop_reason is not None + or example.mask_prefix_mismatch is not False + or example.mask_fallback_reason is not None + ): + raise ValueError("native SFT rejects incomplete or failed prepared examples") + if any(type(value) is not list for value in ( + example.input_ids, example.labels, example.attention_mask, + )): + raise TypeError("native SFT prepared token arrays must be lists") + size = len(example.input_ids) + if not 2 <= size <= config.max_seq_length: + raise ValueError("native SFT row does not fit; truncation is forbidden") + total_tokens += size + if total_tokens > 10000000: + raise ValueError("native SFT prepared token budget exceeded") + if len(example.labels) != size or len(example.attention_mask) != size: + raise ValueError("native SFT token and mask lengths differ") + for token, label, attention in zip( + example.input_ids, example.labels, example.attention_mask, + ): + _integer(token, "token ID", 0, 2**31 - 1) + if type(label) is not int or label not in (-100, token): + raise ValueError("native SFT labels must preserve tokens or ignore them") + if type(attention) is not int or attention != 1: + raise ValueError("native SFT supports only unpadded causal rows") + if not any(label != -100 for label in example.labels[1:]): + raise ValueError("native SFT row has no next-token supervision") + result.append(_Row(row_id, tuple(example.input_ids), tuple(example.labels))) + return tuple(result) + + return PreparedMLXSFT(source, tokenizer_revision, config, split(train), split(validation)) + + +@dataclass(frozen=True) +class MLXSFTBatch: + row_ids: tuple[str, ...] + input_ids: tuple[tuple[int, ...], ...] + labels: tuple[tuple[int, ...], ...] + + +def iter_prepared_batches( + prepared: PreparedMLXSFT, *, split: str, loop: bool = False, +) -> Iterator[MLXSFTBatch]: + """Deterministic train shuffle, ordered full validation, no dropped tail. + + Padding uses token zero only at the right edge; every padded label is ignored. + Causal attention cannot expose that suffix to preceding supervised tokens. + """ + if type(prepared) is not PreparedMLXSFT or split not in ("train", "validation"): + raise ValueError("native SFT split selection is invalid") + if type(loop) is not bool or (loop and split != "train"): + raise ValueError("native SFT validation cannot loop") + rows = getattr(prepared, split) + rng = random.Random(prepared.config.seed) + while True: + order = list(range(len(rows))) + if split == "train": + rng.shuffle(order) + for start in range(0, len(order), prepared.config.batch_size): + batch = tuple(rows[index] for index in order[start:start + prepared.config.batch_size]) + width = max(len(row.input_ids) for row in batch) + yield MLXSFTBatch( + tuple(row.row_id for row in batch), + tuple(row.input_ids + (0,) * (width - len(row.input_ids)) for row in batch), + tuple(row.labels + (-100,) * (width - len(row.labels)) for row in batch), + ) + if not loop: + return + + +def make_mlx_sft_hooks(prepared: PreparedMLXSFT, *, mx, nn): + """Adapt MLX-LM 0.32.2's custom batch/loss ABI without global monkeypatches. + + ``mx``/``nn`` are the qualified child's imported modules. Explicit injection + also permits CPU algebra checks without installing or importing MLX. + """ + if type(prepared) is not PreparedMLXSFT: + raise TypeError("native SFT requires a prepared projection") + + def iterate_batches( + dataset, batch_size, max_seq_length, loop=False, seed=None, comm_group=None, + ): + # Identity is intentional: callers cannot accidentally feed validation to + # training, a transformed sequence, or a different prepared publication. + if dataset is prepared.train: + split = "train" + elif dataset is prepared.validation: + split = "validation" + else: + raise ValueError("native SFT dataset is not a retained split") + if ( + type(batch_size) is not int or batch_size != prepared.config.batch_size + or type(max_seq_length) is not int or max_seq_length != prepared.config.max_seq_length + or (seed is not None and (type(seed) is not int or seed != prepared.config.seed)) + ): + raise ValueError("native SFT iterator controls differ from preparation") + if comm_group is not None and (comm_group.size() != 1 or comm_group.rank() != 0): + raise ValueError("native SFT distributed execution is unqualified") + for batch in iter_prepared_batches(prepared, split=split, loop=loop): + yield mx.array(batch.input_ids, dtype=mx.int32), mx.array(batch.labels, dtype=mx.int32) + + def loss(model, tokens, labels): + targets = labels[:, 1:] + mask = targets != -100 + # Never gather at -100: masking after a negative gather can fail or read + # a real vocabulary entry. Also use where rather than 0 * nonfinite CE. + safe_targets = mx.where(mask, targets, 0) + logits = model(tokens[:, :-1]) + ce = nn.losses.cross_entropy(logits, safe_targets) + ntokens = mask.sum() + total = mx.where(mask, ce, 0).astype(mx.float32).sum() + return total / ntokens, ntokens + + return iterate_batches, loss + + +def run_prepared_mlx_sft( + prepared: PreparedMLXSFT, *, model, optimizer, adapter_file: str, + runtime: MLXRuntimeExpectation, dependency_lock: bytes, + training_callback=None, +) -> None: + """Private child worker hook; no admission, model loading or process launch. + + Invoke only inside the future host-owned qualified child. The caller owns the + bounded process, already prepared model/optimizer, fresh private output path, + artifact verification and callback. This function is not registered in API v1. + """ + if type(prepared) is not PreparedMLXSFT: + raise TypeError("native SFT requires a prepared projection") + from pathlib import Path + + if type(adapter_file) is not str or not Path(adapter_file).is_absolute(): + raise ValueError("native SFT adapter output must be a host-owned absolute path") + inspect_mlx_runtime_candidate(runtime, dependency_lock=dependency_lock) + import mlx.core as mx + import mlx.nn as nn + from mlx_lm.tuner.trainer import TrainingArgs, train + + config = prepared.config + mx.random.seed(config.seed) + iterate, loss = make_mlx_sft_hooks(prepared, mx=mx, nn=nn) + args = TrainingArgs( + batch_size=config.batch_size, iters=config.iterations, + val_batches=-1, max_seq_length=config.max_seq_length, + steps_per_report=config.steps_per_report, + steps_per_eval=config.steps_per_eval, steps_per_save=config.steps_per_save, + adapter_file=adapter_file, grad_checkpoint=False, + grad_accumulation_steps=1, clear_cache_threshold=0, + ) + train( + model=model, optimizer=optimizer, train_dataset=prepared.train, + val_dataset=prepared.validation, args=args, loss=loss, + iterate_batches=iterate, training_callback=training_callback, + ) diff --git a/tuner/runtime/offline_sft_worker.py b/tuner/runtime/offline_sft_worker.py index 2a338dc4..5d236706 100644 --- a/tuner/runtime/offline_sft_worker.py +++ b/tuner/runtime/offline_sft_worker.py @@ -136,6 +136,57 @@ class OfflineSFTWorkerError(RuntimeError): """Fail-closed offline worker closure or bootstrap rejection.""" +_FAILURE_PHASES = frozenset({ + "BOOTSTRAP", "CLOSURE", "BOOTSTRAP_ORIGIN", "ARGUMENTS", + "IMPORT_GUARD", "TRAINER", "POSTCHECK", +}) +_FAILURE_CATEGORIES = frozenset({ + "REJECTION", "IMPORT", "OS", "VALUE", "SYSTEM_EXIT", "OTHER", +}) +# Keep this bootstrap stdlib-only and within its existing closure. These are +# the same finite hooks already emitted for the packaged SFT child. +_TRAINER_MILESTONES = frozenset({ + "CONFIG", "MODEL_SNAPSHOT", "MODEL_LIBRARY_LOAD", "MODEL_SOURCE", + "TOKENIZER_SOURCE", "MODEL_FINALIZE", "LOSS_GUARD", "DATA_PREP", + "LORA_ATTACH", "TRAINER_SETUP", "TRAIN_CALL", "SAVE", "POST_SAVE", + "BOOTSTRAP_ENV", "TORCH_IMPORT", "UNSLOTH_IMPORT", "TRAINER_IMPORT", +}) + + +class _WorkerFailure(OfflineSFTWorkerError): + """Closed diagnostic metadata; never carries the original exception.""" + + def __init__(self, phase: str, category: str, milestone: str | None) -> None: + self.phase = phase if type(phase) is str and phase in _FAILURE_PHASES else None + self.category = ( + category if type(category) is str and category in _FAILURE_CATEGORIES else None + ) + self.milestone = ( + milestone if self.phase == "TRAINER" and type(milestone) is str + and milestone in _TRAINER_MILESTONES else None + ) + super().__init__("OFFLINE_SFT_WORKER_REJECTED") + + +def _failure_category(error: BaseException) -> str: + # Do not inspect error text, attributes, names or traceback frames. Like the + # packaged child, classify only builtin/owned identities in the type's MRO. + bases = type.__getattribute__(type(error), "__mro__") + for kind, category in ( + (OfflineSFTWorkerError, "REJECTION"), (ImportError, "IMPORT"), + (OSError, "OS"), (ValueError, "VALUE"), (SystemExit, "SYSTEM_EXIT"), + ): + if any(base is kind for base in bases): + return category + return "OTHER" + + +def _zero_exit(error: BaseException) -> bool: + return type(error) is SystemExit and ( + error.code is None or (type(error.code) is int and error.code == 0) + ) + + def _reject_constant(value: str) -> None: raise ValueError(f"non-finite JSON number is prohibited: {value}") @@ -771,45 +822,84 @@ def _validate_trainer_arguments(arguments: Sequence[str]) -> None: def run_offline_sft_worker(argv: Sequence[str] | None = None) -> int: - arguments = list(sys.argv[1:] if argv is None else argv) - if not arguments or arguments.pop(0) != "--": - raise OfflineSFTWorkerError("offline SFT bootstrap delimiter is missing") - if ( - sys.flags.isolated != 1 - or sys.flags.no_user_site != 1 - or getattr(sys.flags, "safe_path", False) is not True - or os.environ.get("PYTHONNOUSERSITE") != "1" - or os.environ.get("PYTHONSAFEPATH") != "1" - ): - raise OfflineSFTWorkerError("offline SFT bootstrap is not isolated") - engine_text = os.environ.get("SYNAPTIC_ENGINE_ROOT") - if not isinstance(engine_text, str) or not engine_text: - raise OfflineSFTWorkerError("offline SFT engine root is unavailable") - engine_root = Path(engine_text) - closure = load_offline_sft_worker_environment(os.environ, engine_root=engine_root) - bootstrap = engine_root.joinpath(*PurePosixPath(OFFLINE_SFT_BOOTSTRAP).parts) - if Path(__file__).resolve(strict=True) != bootstrap.resolve(strict=True): - raise OfflineSFTWorkerError("offline SFT bootstrap origin is invalid") - _validate_trainer_arguments(arguments) - verify_loaded_owned_module_origins(closure, engine_root=engine_root) - install_owned_module_guard(closure, engine_root=engine_root) - trainer_directory = engine_root / "Trainers" / "sft" - sys.path[:0] = [str(trainer_directory), str(engine_root)] - trainer = engine_root.joinpath( - *PurePosixPath(OFFLINE_SFT_TRAINER_ENTRYPOINT).parts - ) - sys.argv = [str(trainer), *arguments] - runpy.run_path(str(trainer), run_name="__main__") - verify_loaded_owned_module_origins(closure, engine_root=engine_root) + phase = "BOOTSTRAP" + milestone = None + + def mark_trainer_milestone(value: object) -> None: + nonlocal milestone + milestone = value if type(value) is str and value in _TRAINER_MILESTONES else None + + try: + arguments = list(sys.argv[1:] if argv is None else argv) + if not arguments or arguments.pop(0) != "--": + raise OfflineSFTWorkerError("offline SFT bootstrap delimiter is missing") + if ( + sys.flags.isolated != 1 + or sys.flags.no_user_site != 1 + or getattr(sys.flags, "safe_path", False) is not True + or os.environ.get("PYTHONNOUSERSITE") != "1" + or os.environ.get("PYTHONSAFEPATH") != "1" + ): + raise OfflineSFTWorkerError("offline SFT bootstrap is not isolated") + engine_text = os.environ.get("SYNAPTIC_ENGINE_ROOT") + if not isinstance(engine_text, str) or not engine_text: + raise OfflineSFTWorkerError("offline SFT engine root is unavailable") + engine_root = Path(engine_text) + phase = "CLOSURE" + closure = load_offline_sft_worker_environment(os.environ, engine_root=engine_root) + phase = "BOOTSTRAP_ORIGIN" + bootstrap = engine_root.joinpath(*PurePosixPath(OFFLINE_SFT_BOOTSTRAP).parts) + if Path(__file__).resolve(strict=True) != bootstrap.resolve(strict=True): + raise OfflineSFTWorkerError("offline SFT bootstrap origin is invalid") + phase = "ARGUMENTS" + _validate_trainer_arguments(arguments) + phase = "IMPORT_GUARD" + verify_loaded_owned_module_origins(closure, engine_root=engine_root) + install_owned_module_guard(closure, engine_root=engine_root) + trainer_directory = engine_root / "Trainers" / "sft" + sys.path[:0] = [str(trainer_directory), str(engine_root)] + trainer = engine_root.joinpath( + *PurePosixPath(OFFLINE_SFT_TRAINER_ENTRYPOINT).parts + ) + sys.argv = [str(trainer), *arguments] + phase = "TRAINER" + try: + runpy.run_path( + str(trainer), run_name="__main__", + init_globals={"__packaged_runtime_phase__": mark_trainer_milestone}, + ) + except SystemExit as error: + if not _zero_exit(error): + raise + phase = "POSTCHECK" + verify_loaded_owned_module_origins(closure, engine_root=engine_root) + except BaseException as error: + raise _WorkerFailure(phase, _failure_category(error), milestone) from None return 0 def main() -> int: try: return run_offline_sft_worker() - except (OfflineSFTWorkerError, ImportError): - print("OFFLINE_SFT_WORKER_REJECTED", file=sys.stderr) - return 2 + except _WorkerFailure as error: + # Validate again at the output boundary. Diagnostic metadata is never + # authority to retry a consumed run or to relax closure validation. + if (type(error) is _WorkerFailure + and type(error.phase) is str and error.phase in _FAILURE_PHASES + and type(error.category) is str and error.category in _FAILURE_CATEGORIES): + message = ( + "OFFLINE_SFT_WORKER_REJECTED" + f" phase={error.phase} category={error.category}" + ) + if (error.phase == "TRAINER" and type(error.milestone) is str + and error.milestone in _TRAINER_MILESTONES): + message += f" milestone={error.milestone}" + print(message, file=sys.stderr) + return 2 + except BaseException: + pass + print("OFFLINE_SFT_WORKER_REJECTED", file=sys.stderr) + return 2 if __name__ == "__main__":