mirror of
https://github.com/unslothai/unsloth.git
synced 2026-11-01 11:40:14 +00:00
The earlier comment-trim PRs skipped every module (top-of-file)
docstring outside tests. This condenses 556 of them across
`studio/backend`, `unsloth`, `unsloth_cli`, `scripts` and the top-level
`studio/*.py` helpers, from 5,673 lines to 959. Nothing else changes.
| | before | after |
|---|---|---|
| module docstring lines in the 556 files | 5,673 | 959 |
Each docstring keeps what the module is for plus the rules other code
relies on: fail-open or best-effort behaviour, import-time safety
("stdlib only, safe to import early"), compatibility promises, route
lists, security reasons, ordering rules. For example
`studio/backend/routes/llama.py` now reads `llama.cpp prebuilt update
routes: GET /api/llama/update-status, GET /api/llama/update-changelog,
POST /api/llama/update; fail open; old top-level fields keep shape so
older clients work; whisper and audio.cpp chain as phases.`
## Help text and anything that reads a docstring is left alone
A module docstring was only touched when an AST pass over every Python
file in the repo (nothing imported) found no way for it to be read:
- the module itself never names `__doc__` (`description=__doc__`,
`print(__doc__)`, `globals()`, `sys.modules[__name__]`);
- no file reads `<module>.__doc__` through its imports, and no file that
imports the module reads `.__doc__` on a base that cannot be resolved;
- no file passes the module (or an unresolved name, in a file importing
it) to `inspect.getdoc`, `inspect.getcomments`, `pydoc` or `help()`, or
the whole module to `inspect.getsource`;
- no file reads `__doc__` through `getattr`/`vars` while naming the
module;
- no 20+ character fragment of the docstring appears verbatim in another
file that names or imports the module (a source-reading test or tool
anchoring on it).
80 modules failed one of these and are unchanged, including every
`scripts/` file whose argparse help is `description=__doc__`,
`studio/install_python_stack.py`, and modules that tests read whole with
`inspect.getsource` (`core/inference/diffusion.py`, `llama_cpp.py`,
`video.py`, ...).
## How it was checked
- **Only module docstrings changed:** for every changed file the module
AST, with the docstring text blanked, is identical to `main` (every
other statement, docstring and string literal is byte-identical), and
the file is on the safe list. `comment_tools.py check --strict-ext
--allow-docstring-edits` passes on all 556 files.
- **Help output, byte for byte against `main`:** `unsloth --help` and
all 40 subcommand help pages, plus `python <file> --help` for all 65
scripts with an argparse/click/typer parser and a `__main__` block. Two
scripts cannot print help in this environment on either side
(`kv_cache_multiturn_benchmark.py` needs a GPU to import,
`kernel_install.py` must run as a module); `python -m
utils.kernel_install --help` is identical on both, and the benchmark's
parser has no description.
- **Tests, `main` vs this branch on the same commit:** every backend
test file that reads source text, docstrings or line numbers (905 files:
`read_text`, `open(`, `getsource`, `linecache`, `lineno`, `__doc__`,
`get_docstring`, `ast.parse`, `tokenize`), the full root and
`unsloth_cli` suites, and every frontend test that reads a `.py` file.
Failing IDs match `main`; the few one-off differences pass 3/3 alone on
both sides.
- **Wording:** each rewrite was reviewed against its original for a lost
rule, a reversed condition or an invented claim, and each proposed
correction was independently checked against the original before it was
applied.
82 lines
2.9 KiB
Python
82 lines
2.9 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-only
|
|
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
|
|
|
"""SAM 3 / 3.1 segmenter builder; reuses diffusion_segment_quant so Studio's loader checks pass."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
BACKEND = Path(__file__).resolve().parent.parent / "studio" / "backend"
|
|
|
|
|
|
def main(argv = None) -> int:
|
|
p = argparse.ArgumentParser()
|
|
p.add_argument("--src", required = True, help = "transformers-format Sam3Model directory (bf16)")
|
|
p.add_argument(
|
|
"--base-model-id", required = True, help = "the repo the artifact belongs to (recorded)"
|
|
)
|
|
p.add_argument(
|
|
"--source-revision", default = None, help = "the repo commit the source was taken from"
|
|
)
|
|
p.add_argument("--scheme", required = True, choices = ("fp8", "int8"))
|
|
p.add_argument("--convrot-groupsize", type = int, default = 0)
|
|
p.add_argument("--out", required = True, help = "output .safetensors path")
|
|
p.add_argument("--device", default = "cuda")
|
|
args = p.parse_args(argv)
|
|
if not args.out.endswith(".safetensors"):
|
|
print("error: --out must end in .safetensors", flush = True)
|
|
return 2
|
|
|
|
sys.path.insert(0, str(BACKEND))
|
|
import torch
|
|
from transformers import Sam3Model
|
|
|
|
from core.inference.diffusion_segment_quant import quantize_segmenter_, write_prequant_file
|
|
|
|
started = time.time()
|
|
model = Sam3Model.from_pretrained(args.src, dtype = torch.bfloat16, device_map = args.device).eval()
|
|
quant = quantize_segmenter_(model, args.scheme, convrot_group = args.convrot_groupsize)
|
|
out = Path(args.out)
|
|
out.parent.mkdir(parents = True, exist_ok = True)
|
|
tmp = out.with_name(out.name + ".tmp")
|
|
metadata = write_prequant_file(
|
|
str(tmp),
|
|
model,
|
|
quant,
|
|
base_model_id = args.base_model_id,
|
|
source_revision = args.source_revision,
|
|
)
|
|
tmp.replace(out)
|
|
fmt = metadata["format"]
|
|
digest = hashlib.sha256()
|
|
with open(out, "rb") as fh:
|
|
for chunk in iter(lambda: fh.read(1 << 24), b""):
|
|
digest.update(chunk)
|
|
print(
|
|
json.dumps(
|
|
{
|
|
"out": str(out),
|
|
"bytes": out.stat().st_size,
|
|
"sha256": digest.hexdigest(),
|
|
"format": fmt,
|
|
"scheme": args.scheme,
|
|
"quantized_linears": quant["quantized_linears"],
|
|
"rotated_linears": len(quant.get("activation_rotation_fqns") or ()),
|
|
"exclude_name_tokens": quant["exclude_name_tokens"],
|
|
"fingerprinted": metadata["fingerprint"]["count"],
|
|
"seconds": round(time.time() - started, 1),
|
|
}
|
|
),
|
|
flush = True,
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|