mirror of
https://github.com/unslothai/unsloth.git
synced 2026-08-17 21:03:59 +00:00
* Refuse a GGUF export that cannot fit, and use Kaggle's large /tmp overlay Two disk failures seen in real notebook runs, fixed in front of the writes rather than after them. Kaggle exhausts /kaggle/working, which is about 20GB, while the overlay mounted at /tmp on the same kernel has over a terabyte free. A merge or GGUF export pointed at a relative path under the working directory that cannot fit there now goes to /tmp instead, with one printed line saying where the files are and that /tmp is scratch space Kaggle does not save as kernel output. An absolute path is never moved, because silently relocating a directory the caller named would be worse than the disk error it avoids, and a push_to_hub save_directory is a repo id rather than a path so it is left alone entirely. Gemma4 26B A4B Vision, Gemma4 31B Vision and Qwen3 32B each trained, ran inference and wrote a complete merged_16bit, then died partway through a GGUF shard. The size in front of them counted the model twice; the real peak is the pre-warmed base in the Hugging Face cache, the merge, the intermediate GGUF and the quants, all on one filesystem at once. _preflight_gguf_disk sizes all four before the merge starts. When the export fits but a cached base as well does not, it drops the pre-warm rather than refusing, because that is an optimization for the next export and this one still runs. When nothing fits it raises with the two numbers and what to do about them. Neither guard blocks on a guess: an unmeasurable model or an unmeasurable filesystem proceeds exactly as before, as does UNSLOTH_DISK_PREFLIGHT=0. Kaggle detection moves to unsloth_zoo.disk_utils, with a fallback for an older installed unsloth_zoo that keeps the environment question answered correctly and turns the new guard into a no-op. 30 new tests, each watched to fail against a perturbed fix. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Make the Kaggle merge preflight measurable, and size what the export really writes The merge preflight passed keep_intermediate_gguf to unsloth_zoo.disk_utils.estimate_gguf_export_bytes, which has no such parameter, so every call raised TypeError into the surrounding except and no save_pretrained_merged was ever redirected on Kaggle. A merge writes no GGUF at all, so it is now sized with model_16bit_bytes rather than the GGUF export estimate, which always prices an intermediate conversion. Also, on the same path: - every compressed export (fp8, nvfp4, mxfp8, w4a16, ...) keeps the 16-bit merge and writes a quantized sibling, so all of them are measured, and the sibling is counted, not just mxfp4's merge; - save_method spellings are normalized the way unsloth_save_model normalizes them, so "merged 16bit" is measured like merged_16bit; - the non-PEFT fallback save_pretrained writes a full checkpoint, so the GGUF estimate counts it instead of assuming a non-PEFT model has one on disk; - imatrix_file=False disables the imatrix in _resolve_imatrix_file, so the preflight no longer sizes a two-pass conversion for it; - Kaggle and Colab never pre-warm the hub cache, so the redirect decision stops pricing a cache copy that cannot exist there and sending an export that fits in /kaggle/working to /tmp, which is not kept as output. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Size the Kaggle merge redirect the way the merge itself measures it The redirect asked for the raw tensor bytes, but merge_and_overwrite_lora compares the save against int(free * 0.95), so a merge that only just fit was left in /kaggle/working and then refused outright instead of moving to /tmp. Ask for the same effective figure. Also size a quantized sibling by what a weight-only scheme actually shrinks: compressed-tensors and torchao quantize Linear weights only, so the embeddings and an untied lm_head stay 16-bit. The torchao portable exports now take the redirect too; their 16-bit merge is staged in a temp directory, so only the sibling is priced at save_directory. And the non-PEFT GGUF fallback save_pretrained()s the model at its own dtype, which for a float32 load is twice what the estimate budgeted. * Do not redirect a torchao export onto a /tmp that its staging merge also fills The portable torchao exports now reach the Kaggle redirect, but the sibling is the only artifact the redirect sizes. _unsloth_save_torchao merges into tempfile.mkdtemp() and keeps that 16-bit staging checkpoint until quantization finishes, and on a Kaggle kernel tempfile resolves to the same /tmp the redirect moves the sibling to, so both are on the destination at once. A working directory with room for the sibling but not the 5% the merge guard reserves therefore had its export moved to a /tmp that then ran out, when leaving it in place would have worked. The staging bytes are checked against the destination rather than added to need_bytes: nothing stages in the working directory, so charging it there would relocate exports that fit into /tmp, which is not kept as notebook output. The check can only cancel a redirect, never cause one. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Stat the real redirect destination in the torchao staging test The staging guard's own tests stub _same_filesystem, so nothing exercised the os.stat pair it is built on, and nothing recorded why stat-ing the destination is safe: kaggle_tmp_redirect returns a message only after os.makedirs has succeeded, and the fallback in unsloth/disk_utils.py never returns one, so the guard is unreachable with a destination that does not exist. Two tests. The first cancels a redirect with _same_filesystem left unstubbed and a real directory created under the tempfile default. Cancelling is the one outcome the helper cannot reach by accident, since every failure inside it, os.stat included, returns True and takes the redirect, so a real destination that is really rejected proves both stats resolved. The second pins the invariant the first depends on, against whichever kaggle_tmp_redirect is installed. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Preflight a full model saved as LoRA, and measure the GGUF sibling's disk Two gaps in the Kaggle disk guard, both of which let an export pass a check that measured the wrong thing. A model with no adapter saved with save_method="lora" is not a LoRA save: unsloth_generic_save and unsloth_save_model both fall back to save_pretrained and write the entire model, so a full fine-tune asked for "lora" fills /kaggle/working exactly like a merge. The preflight skipped it on the method name alone. It is now sized whenever the model is not a PeftModel, from the parameters' own storage rather than as a 16-bit merge, because that fallback casts nothing: an fp32 model writes four bytes per parameter and a 4-bit one its packed storage. A real PeftModel still writes adapters and is still skipped, and a model that cannot be measured is left exactly as before. The GGUF files land in save_directory + "_gguf", which is a sibling, so they sit on the parent's filesystem. That is the same disk as save_directory unless that path is itself a mount point or a symlink onto another one, and then the aggregate estimate was checked against a filesystem the quants never touch. The sibling is now probed separately and compared against the export minus the checkpoint, which is what actually goes there. Only a strictly tighter sibling can change the outcome, so a single filesystem behaves exactly as before, an unmeasurable path leaves the decision alone, and an estimator that cannot answer the new question leaves the main guard standing. The export and the preflight now share one definition of that directory so the two cannot drift. * Charge the compressed sibling for every module the recipe refuses to quantize The compressed recipe ignores more than the embeddings: `lm_head`, `re:.*\.linear_attn\..*`, `re:.*\.visual\..*`, `re:.*mtp.*`, and on an MoE `re:.*\.gate$` and `re:.*\.shared_expert_gate$`. The sizing helper walked only `get_input_embeddings` and `get_output_embeddings`, so a VLM's vision tower, a Qwen3-Next hybrid's linear attention, an MTP head and the MoE routers were all priced at 4 or 8 bits for bytes the export copies across at 16. That under-counts `need`, and an under-counted `need` is how a merge that should have gone to /tmp stays in /kaggle/working. The four lines that build `ignore` are now a module-level `compressed_ignore_patterns(config)` in `_compressed_quantize.py`, which `main()` calls and which `save.py` reads, so the sizing cannot drift from the recipe again. That module's imports stay stdlib only. `_unquantized_parameter_bytes` takes the pattern list and adds the 16-bit bytes of every module the recipe ignores, matching compressed-tensors' `match_name`: a `re:` prefix is `re.match` against the fully qualified module name, a plain entry is an exact name or a parent class name. Ids are deduplicated against the embeddings, so an `lm_head` that is also `get_output_embeddings()` is counted once and a nested match under an already counted tower is not counted twice. The torchao branch passes no patterns. `_unsloth_save_torchao` quantizes with a bare weight-only config and has no ignore list, so charging it these would over-count, and over-counting relocates an export that fits into a /tmp that Kaggle does not keep as notebook output. Every new path degrades to the old estimate rather than raising: a missing or renamed symbol, a model whose modules cannot be walked, a module that will not answer `parameters()`, or an unparseable pattern. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Charge each filesystem only for what the export writes to it The GGUF preflight measured the sibling directory's filesystem but still compared the aggregate estimate against the checkpoint's, so a save directory that is a mount point or a symlink onto another mount had the intermediate conversion and every quant charged to a disk they never touch. A checkpoint that fits, with a sibling filesystem that has ample room for the quants, was refused. Both halves now hang off one predicate computed once, so they cannot disagree about whether the storage is split. When it is split the checkpoint portion, need minus need_sibling, is charged to the save directory's free space and need_sibling to the sibling's; when it is not, nothing changes. The predicate is the device id of the filesystem each probe measures, rather than the sibling reporting less free space, because two disk_usage calls on one filesystem can disagree when something else writes between them and reading that as two filesystems would charge a single-filesystem export the larger of its two halves instead of their sum. An unreadable or zero device id means not split. The sibling refusal no longer requires the sibling to be the tighter of the two: once the checkpoint is charged only its own portion, a sibling that is roomier than the save directory and still short of need_sibling has to be caught there, because the aggregate comparison that used to catch it is gone. The new branch can only turn a refusal into a pass or change which message is raised, and it runs after the redirect has been decided, so it still cannot cause one. An estimator that cannot size the sibling leaves need_sibling at zero, which makes the checkpoint portion the whole estimate again. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Charge the GGUF export for what is on disk at once, and where Three corrections to the disk preflight, all of them cases where the figure it compares against free space is not the figure the export produces. A disposable merge is not charged for all three artefacts at once. `_free_merge_if_disk_is_tight` deletes this export's own merge once the intermediate GGUF exists and before the first quantize pass, so the peak is the larger of "merge plus intermediate" and "intermediate plus quants", not their sum. Nemotron-3-Nano-30B-A3B on a 132GB disk peaks at 128GB and runs; the aggregate 147GB refused it. `_preflight_gguf_disk` takes the same `merge_is_disposable` flag the reclamation acts on, and relaxes only when a quantize pass follows, only on one filesystem (the reclamation declines across two), only when the output directory holds no weights this export did not write, and only when the peak is lower than the aggregate. The working directory the initial conversion writes to is measured. `convert_to_gguf` passes a bare `--outfile`, which llama.cpp resolves against the process CWD, and the finished file is moved into the `_gguf` directory afterwards. So a Kaggle export redirected to /tmp still writes its largest staging artefact into the 20GB working directory, on a filesystem nothing here looked at. When that directory is a different filesystem from where the file ends up, it now has to hold the intermediate, and the export is refused with the directory named if it cannot. Ignored modules are sized from their logical shapes. `model_16bit_bytes` sizes the merge through unsloth_zoo's `logical_numel`, which reads `quant_state.shape`; `_unquantized_parameter_bytes` was subtracting a figure built from `numel()`, which on a 4-bit `Params4bit` is the packed uint8 count and roughly half. A Qwen3-Next linear-attention subtree was priced at 8 bits for bytes the export writes at 16, and an under-counted need is a Kaggle redirect that never happens. It now calls `logical_numel` itself, through `named_parameters` so the name reaches it, which is the only way MXFP4 packing is identifiable. Every new path degrades to the previous behaviour rather than raising: an unreadable working directory, an unmeasurable free figure, an estimator that cannot size a phase, a directory that cannot be listed, a module that cannot name its parameters. * Keep the merge guard's reserve when the estimate is split `_preflight_gguf_disk` charges the checkpoint's own portion to the filesystem holding `save_directory` once the GGUF sibling is on another one. That portion is two bytes per parameter exactly, and `merge_and_overwrite_lora` refuses to write a merge unless `free * 0.95` covers it, so 16GB of checkpoint on 16GB of disk passed the preflight and died in the merge seconds later. The aggregate branch never needed the reserve because it charges the quants as well. Clamped at `need`, so the split still cannot refuse an export the aggregate allowed, and skipped where no merge is written or where the sibling could not be sized and the figure is a fallback rather than a checkpoint. Also size a full-model `"lora"` save from the caller's `state_dict` when there is one. `save_pretrained` writes that dict, only `"16bit" in save_method` casts it, and both `_preflight_merge_disk` call sites accept one and document `"lora"`, so an fp32 dict over fp16 parameters was priced at half. * Say which disk ran out when the staging merge is on its own filesystem `_destination_holds_torchao_staging` asks whether the redirect DESTINATION can hold the staging merge as well, which is the whole question on Kaggle, where `tempfile` and the destination are both /tmp. When they are separate mounts it returns True and nothing has measured the staging filesystem at all, so a 4GB tmpfs is handed a 60GB merge and `_unsloth_save_torchao` dies inside `tempfile.mkdtemp` without naming TMPDIR. A warning rather than a refusal or a cancelled redirect: the preflight never raises, and the staging merge is written to TMPDIR whether or not the export was relocated, so declining the move leaves the identical failure and puts the output on the smaller disk too. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Compare filesystems by the ancestor a fresh destination writes to `_same_filesystem` stat-ed both paths directly, and `_preflight_merge_disk` runs before anything has been created, so on a first torchao export the destination was a name and not a directory. The stat raised FileNotFoundError, the helper's broad handler swallowed the whole probe, and an undersized TMPDIR went unmentioned in exactly the case the warning was added for. It now resolves the nearest existing ancestor, which is the filesystem the write really lands on and the same resolution `free_bytes` and `_filesystem_id` already use. A path neither side can identify still raises, because both callers read that as "cannot tell" and cancel the probe rather than guess. * Reserve in the TMPDIR warning what the staging merge really needs The warning compared the free space on a separate TMPDIR against the raw size of the staging merge, but that merge is written by `merge_and_overwrite_lora`, which refuses to write anything `int(free * 0.95)` does not cover. Between the merge's size and that size over 0.95 the export therefore died with "Failed saving - no disk space left" while the diagnostic that exists to name the disk stayed silent, and on a separate TMPDIR the merge's own temp-folder fallback lands on the same filesystem, so nothing recovers it. Compared against the reserved figure now, the same one `_preflight_merge_disk` asks the redirect for, and the printed size is that figure so the number and the threshold agree. * Charge the merge guard's reserve only where that guard runs The split branch added `merge_and_overwrite_lora`'s 5% reserve to the checkpoint whenever `needs_merge` was set, but `needs_merge` is also true for a non-PEFT model with no reusable local `_name_or_path`: the GGUF path has to write a checkpoint there too, and it writes it with a bare `self.save_pretrained`, which consults no guard and reserves nothing. A filesystem holding exactly that checkpoint was refused for headroom the writer never asks for. Gated on the model really being a PEFT one, which is the only case that reaches the merge. The tests that cover the reserve now use a model the preflight recognises as PEFT, so they exercise the path the reserve belongs to. * Follow the reused checkpoint when an unwritable CWD moves the conversion `convert_to_gguf` passes a bare `--outfile`, and when the process CWD cannot be written to llama.cpp's output is redirected into the converter's input folder. That input folder is not always `save_directory`: a non-PEFT model with a local `_name_or_path` is converted from its own checkpoint, which `unsloth_save_pretrained_gguf` swaps in before calling `save_to_gguf`. The preflight probed the requested output instead, so with the two on different filesystems it measured a disk nothing was written to while the intermediate GGUF filled the checkpoint's. The conversion probe now resolves the same input folder the export will use, by the same condition `_gguf_writes_16bit_checkpoint` already reads. * Charge a split export for the conversion that lands beside its checkpoint The split branch charges the filesystem holding `save_directory` for the checkpoint alone, and the working-directory check charged whatever filesystem the intermediate conversion is written to for the conversion alone. When those are the same disk - `save_directory` a mount whose `_gguf` sibling is elsewhere, with the process working directory on the mount - both artefacts sit on it at once and neither check ever added them up. A 60GB checkpoint and a 60GB conversion both passed on 100GB, and then it filled. The conversion is now added to the split branch's figure when it lands on that same filesystem. `_shares_filesystem` answers that, and unlike `_on_separate_filesystems` it says no to a path it cannot identify, because here the answer adds a charge rather than removing one. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Reserve for the merge alone, and follow the dict the writer follows Four corrections to where the merge guard's 5% reserve is charged, and to how a caller's state dict is read. `_preflight_merge_disk` reserved around the whole estimate. Only the 16-bit merge is written by `merge_and_overwrite_lora`, so only that part is measured by its `free * 0.95`. A full-model `"lora"` save goes straight through `save_pretrained`, and the torchao path merges into a temp directory and leaves only the quantized sibling here, so neither pays a reserve at all. On a 20GB Kaggle working directory the over-charge relocated every output between 19.0GB and 20.0GB into a /tmp the kernel does not keep. The GGUF split path compared the checkpoint plus its cache copy against the reserved checkpoint with `max`, when the cache is written first and is still resident when the guard runs. 16GB of checkpoint with a 14GB cache on 30.5GB passed at 30GB and then the merge saw 16.5GB and refused 16GB. Added, this band drops the optional pre-warm rather than failing the export. `_full_model_checkpoint_bytes` selected the caller's dict on truthiness, while `unsloth_generic_save` forwards it on `is not None`. So an explicitly empty dict wrote no tensors and was priced as the whole resident model. And compressed and torchao build their output as `save_directory + "-<suffix>"`, which is lexical, so a symlinked `save_directory` puts the sibling on the filesystem the probe never asked about. Warned rather than redirected: the caller derives the sibling from whatever this returns, so a redirect already moves both, and the uncovered case is the one where no redirect fires. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Follow the dict a 16-bit save writes, and phase the merge reserve The generic writer only builds a state dict when it was handed none, so a caller-supplied one is what the 16-bit checkpoint costs; the other writer rebuilds it from the merged layers and the resident model is still the right figure there. A split export whose conversion lands beside its checkpoint was charged the merge reserve and the conversion together. The merge guard runs before the conversion is written, so the requirement is the taller of the two phases: 60GB of merge and 60GB of conversion fit in 122GB, and the sum asked 123.2GB and refused. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Ask each writer what it does with the dict, and the config what dtype it holds Four sizing inputs the preflights were guessing at. `unsloth_save_model` rebuilds the state dict from the merged layers only on the path that walks `.model.layers`. Anything else takes its generic fallback, which calls `save_pretrained(**save_pretrained_settings)` with the caller's dictionary still in it and with no cast, so the checkpoint is that dictionary's own bytes at its own dtypes. A 4GB fp32 dictionary was priced as a 2GB merge. compressed-tensors and torchao merge to 16 bits through `unsloth_generic_save` as well, forwarding the same dictionary, so their kept or staged merge is sized from it too and not only a literal `merged_16bit` request. `_preflight_gguf_disk` was left on its `model_dtype = "f16"` default while `save_to_gguf` reads the config. `estimate_gguf_export_bytes` drops a requested output that equals the initial conversion, so a bf16 model asked for ["f16", "q4_k_m"] was charged one 16-bit file where the export writes a bf16 intermediate and a separate f16 output: 37.2GB asked against 52.4GB written on Qwen3-8B. And the pre-warmed base model is charged to the filesystem that actually holds the Hugging Face cache. `save_directory` being a mount is the premise of the split branch, and then `~/.cache` and the lexical `_gguf` sibling are both on the parent disk: charging the checkpoint's filesystem dropped a pre-warm that had room and let the sibling accept the GGUF files alone on a disk the base was about to be downloaded onto. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Drop bf16 to f16 before sizing, and charge the cache where the conversion lands Two filesystems' worth of undercount, both in the GGUF preflight. `save_to_gguf` switches a bf16 initial conversion to f16 on hardware with no bf16, and it does so AFTER resolving one. The preflight only normalized the dtype it was told, so an explicit `first_conversion="bf16"` went through untouched, and so did the single direct-convert method `_choose_first_conversion` hands back. The estimate omits an output equal to the initial conversion, so `["bf16"]` on a T4 was priced as one 16-bit file where the export writes an f16 intermediate and a separate bf16 output: 30.5GB charged against 45.8GB written on Qwen3-8B, 15.3GB short. The other is a third filesystem. With the output on an external drive and the writable CWD sharing the machine's own disk with the Hugging Face cache, the conversion check charged the intermediate alone. The pre-warm downloads the base onto that same disk first and leaves it there, and its own gate does not catch it either: it asks for two base copies free, and an f32 conversion is two base copies on its own. 38.1GB free clears the pre-warm's 30.5GB threshold and the 30.5GB conversion check, then the conversion writes 30.5GB into the 22.8GB the cached base left. The cache is now resolved once, above the split, and charged on the conversion's filesystem too - dropping the pre-warm rather than refusing, since the raise for a conversion that does not fit at all still runs first. The pre-warm's skip message travels with the flag now, because more than one filesystem can clear it and each has to name the one it measured. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Reserve the merge guard's 5% only where the guard runs `merge_and_overwrite_lora` and its `free * 0.95` have one caller here: `unsloth_generic_save`, on its adapter branch. The preflight was charging the reserve to every 16-bit merge that was not written verbatim, including `unsloth_generic_save_pretrained_merged`'s no-adapter save, which casts the supplied dict and writes it with a bare `save_pretrained`. The reserve is now driven by whether that writer runs, which is separate from the sizing: a compressed export really is cast to two bytes by the same writer and keeps that sizing whether or not there is an adapter to merge. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Charge the cache pre-warm to the filesystem it is written to The single-filesystem branch of the GGUF preflight added the pre-warmed base copy to need_with_cache and then compared that total against the free space on save_directory, whatever filesystem the Hugging Face cache is actually on. HF_HOME on a data volume is the ordinary layout on a machine with more than one disk, and there the cached base never lands on the export disk at all. Measured with a 60GB checkpoint, 40GB of quants and a 60GB cached base on 120GB free: the export needs 100GB on that disk and fits, but the fictitious cache copy takes the ask to 160GB and the pre-warm is dropped, so the next export downloads the whole base again. That is the re-download the pre-warm exists to stop. With the cache on its own filesystem the ask is now 100GB and the pre-warm survives; with the cache on the export disk it is still 160GB and the pre-warm is still dropped. The split branch already did this accounting for the checkpoint and the sibling, so the predicate moves above the split and both branches read one answer. The figure can only fall, and only the pre-warm decision reads it, so no export that fit before is refused now. An unresolvable cache stays charged where it was. * Size the Kaggle redirect from the phased peak, and say when a cancelled one leaves no room Two holes on the Kaggle side of the preflight. The GGUF redirect was asked for the aggregate while the refusal below it reads the phased peak of a disposable merge. On the Nemotron shape - a 63GB merge, a 60GB intermediate, an 18GB Q4_K_M - the ask was 141.0GB and the peak is 123.0GB, so a /kaggle/working with 132.0GB free measured too small and the export was relocated to /tmp, which the kernel does not keep as notebook output. The redirect now asks the same 123.0GB, under the same predicates as the branch that lowers the figure, read against the directory before any move. It can only ever lower the ask. A cancelled torchao redirect was silent. With a 10GB staging merge and a 5.0GB fp8 sibling, /kaggle/working at 4.0GB free and /tmp at 12.0GB, the redirect fires because the sibling does not fit here and is then cancelled because /tmp cannot hold the sibling and the staging merge together. That is still the right move, but it hands the export back a filesystem measured too small for it, and nothing downstream measures that: the merge is staged in TMPDIR, so the merge guard's free * 0.95 covers the staging disk only, and the sibling is written at the very end of a long quantization. It now says so, with both figures. A warning and not a refusal, like the two warnings beside it: this preflight never raises and the sibling is an estimate. An unmeasured move (UNSLOTH_KAGGLE_USE_TMP=1) stays silent. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Re-ask the Kaggle redirect at the phased peak when this directory cannot be reclaimed A save directory that already holds a checkpoint yields nothing for _free_merge_if_disk_is_tight to reclaim, so the redirect above it is asked for the aggregate. The move, though, writes the merge into a directory of the export's own, where the reclamation is available: on the Nemotron-3-Nano-30B-A3B shape (63GB merge, 60GB intermediate, 18GB Q4_K_M) that is a 141GB ask against a 130GB overlay, declined, and the export refused in place at 141GB on 100GB free, when relocating peaks at 123GB and fits. The first ask keeps the aggregate. Only after the move is declined, and only where this filesystem is already short of the figure the refusal will read, is it asked again at the peak. Asking the peak outright instead is a regression: 130GB free here holds the peak but not the aggregate, so the lower ask cancels a move the export needs and the refusal reads 141GB anyway. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: danielhanchen <unslothshared@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> |
||
|---|---|---|
| .. | ||
| gpt-oss-merge | ||
| language_models | ||
| non_peft | ||
| text_to_speech_models | ||
| vision_models | ||
| run_offline_gguf_integration.py | ||
| test_compressed_export_schemes.py | ||
| test_export_api_surface.py | ||
| test_export_dispatch.py | ||
| test_fix_sentencepiece_gguf_robustness.py | ||
| test_fix_sentencepiece_tokenizer_guard.py | ||
| test_gguf_disk_preflight.py | ||
| test_gguf_export_and_inference.py | ||
| test_gguf_single_pass_export.py | ||
| test_imatrix_export.py | ||
| test_is_gpt_oss_detection.py | ||
| test_is_vlm_detection.py | ||
| test_llm_compressor_install_pin.py | ||
| test_normalize_tied_weights_keys.py | ||
| test_offline_gguf_real_cache_integration.py | ||
| test_offline_gguf_vlm_tokenizer_7481.py | ||
| test_patch_saving_none_tokenizer.py | ||
| test_preserve_tokenizer_eos_token.py | ||
| test_prewarm_base_model_hub_cache.py | ||
| test_quant_method_none_normalization.py | ||
| test_qwen3_5_vlm_full_finetune_key_remap.py | ||
| test_save_shell_injection.py | ||
| test_save_subprocess_utf8_encoding.py | ||
| test_torchao_remote_code_consent.py | ||
| test_unsloth_save.py | ||