mirror of
https://github.com/kvcache-ai/ktransformers.git
synced 2026-10-17 05:11:19 +08:00
release: build post3 from frozen post2 carriers
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build the GLM config hotfix wheels from the verified post2 carriers.
|
||||
|
||||
The CUDA payload and unchanged runtime files are copied byte-for-byte from the
|
||||
published post2 wheels. Only the reviewed GLM Python source and distribution
|
||||
metadata are changed. This keeps the post2 carrier stack intact.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import base64
|
||||
import csv
|
||||
import hashlib
|
||||
import json
|
||||
import shutil
|
||||
import tempfile
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
POST2_HASHES = {
|
||||
"kt_kernel": "b44b68effe42f7cfbfe9bb8e07af57ef29194c3648e20a1fdd06273b42aca7ca",
|
||||
"ktransformers": "1bf1e51e115314388e41aeed5d7c380985bf67a448e8133af1e7ff81de5d9f8f",
|
||||
"sglang": "b13705d35c577530efaec915b02ba66a6bf67f88a5429f4758bb59885e723971",
|
||||
"transformers": "b8e7c1baeb19123b251dfb4f6e6c2be615abd29b6feb23113deeaef5e7175811",
|
||||
}
|
||||
MAX_PYPI_WHEEL_SIZE = 104_000_000
|
||||
|
||||
|
||||
def sha256(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as stream:
|
||||
for block in iter(lambda: stream.read(8 * 1024 * 1024), b""):
|
||||
digest.update(block)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
def record_hash(path: Path) -> str:
|
||||
digest = hashlib.sha256(path.read_bytes()).digest()
|
||||
return "sha256=" + base64.urlsafe_b64encode(digest).rstrip(b"=").decode()
|
||||
|
||||
|
||||
def write_record(root: Path, dist_info: Path) -> None:
|
||||
record = dist_info / "RECORD"
|
||||
rows = []
|
||||
for path in sorted(root.rglob("*")):
|
||||
if path.is_file() and path != record:
|
||||
rows.append(
|
||||
(path.relative_to(root).as_posix(), record_hash(path), str(path.stat().st_size))
|
||||
)
|
||||
rows.append((record.relative_to(root).as_posix(), "", ""))
|
||||
with record.open("w", newline="") as stream:
|
||||
csv.writer(stream, lineterminator="\n").writerows(rows)
|
||||
|
||||
|
||||
def pack_wheel(root: Path, output: Path) -> None:
|
||||
with zipfile.ZipFile(
|
||||
output, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9
|
||||
) as wheel:
|
||||
for path in sorted(root.rglob("*")):
|
||||
if path.is_file():
|
||||
wheel.write(path, path.relative_to(root).as_posix())
|
||||
if output.stat().st_size >= MAX_PYPI_WHEEL_SIZE:
|
||||
raise RuntimeError(f"PyPI wheel is too large: {output} ({output.stat().st_size} bytes)")
|
||||
|
||||
|
||||
def replace_once(text: str, old: str, new: str, *, label: str) -> str:
|
||||
if text.count(old) != 1:
|
||||
raise RuntimeError(f"Expected exactly one {label}: {old!r}")
|
||||
return text.replace(old, new, 1)
|
||||
|
||||
|
||||
def unpack(input_wheel: Path, root: Path) -> None:
|
||||
with zipfile.ZipFile(input_wheel) as wheel:
|
||||
wheel.extractall(root)
|
||||
|
||||
|
||||
def build_sglang(input_wheel: Path, source_file: Path, output_dir: Path) -> Path:
|
||||
with tempfile.TemporaryDirectory() as temp_name:
|
||||
root = Path(temp_name)
|
||||
unpack(input_wheel, root)
|
||||
old_dist_info = root / "sglang_kt-0.7.0.post2.dist-info"
|
||||
new_dist_info = root / "sglang_kt-0.7.0.post3.dist-info"
|
||||
if not old_dist_info.is_dir():
|
||||
raise RuntimeError(f"Missing expected dist-info: {old_dist_info}")
|
||||
|
||||
destination = root / "sglang/srt/models/glm5_next_dsa.py"
|
||||
old_source_hash = sha256(destination)
|
||||
new_source_hash = sha256(source_file)
|
||||
if old_source_hash == new_source_hash:
|
||||
raise RuntimeError("The reviewed GLM hotfix is not different from post2")
|
||||
|
||||
payload = root / "sglang_kt_sgl_kernel_payload/payload.part"
|
||||
payload_hash = sha256(payload)
|
||||
shutil.copy2(source_file, destination)
|
||||
if sha256(payload) != payload_hash:
|
||||
raise RuntimeError("SGLang payload changed while applying the Python hotfix")
|
||||
|
||||
old_dist_info.rename(new_dist_info)
|
||||
metadata = new_dist_info / "METADATA"
|
||||
metadata.write_text(
|
||||
replace_once(
|
||||
metadata.read_text(),
|
||||
"Version: 0.7.0.post2",
|
||||
"Version: 0.7.0.post3",
|
||||
label="SGLang version",
|
||||
)
|
||||
)
|
||||
write_record(root, new_dist_info)
|
||||
output = output_dir / (
|
||||
"sglang_kt-0.7.0.post3-py3-none-manylinux_2_35_x86_64.whl"
|
||||
)
|
||||
pack_wheel(root, output)
|
||||
return output
|
||||
|
||||
|
||||
def build_ktransformers(input_wheel: Path, output_dir: Path) -> Path:
|
||||
with tempfile.TemporaryDirectory() as temp_name:
|
||||
root = Path(temp_name)
|
||||
unpack(input_wheel, root)
|
||||
old_dist_info = root / "ktransformers-0.7.0.post2.dist-info"
|
||||
new_dist_info = root / "ktransformers-0.7.0.post3.dist-info"
|
||||
if not old_dist_info.is_dir():
|
||||
raise RuntimeError(f"Missing expected dist-info: {old_dist_info}")
|
||||
|
||||
payload = root / "ktransformers_sgl_kernel_payload/payload.part"
|
||||
payload_hash = sha256(payload)
|
||||
old_dist_info.rename(new_dist_info)
|
||||
metadata = new_dist_info / "METADATA"
|
||||
text = metadata.read_text()
|
||||
text = replace_once(
|
||||
text,
|
||||
"Version: 0.7.0.post2",
|
||||
"Version: 0.7.0.post3",
|
||||
label="KTransformers version",
|
||||
)
|
||||
text = replace_once(
|
||||
text,
|
||||
"Requires-Dist: sglang-kt==0.7.0.post2; extra == \"sglang\"",
|
||||
"Requires-Dist: sglang-kt==0.7.0.post3; extra == \"sglang\"",
|
||||
label="SGLang dependency",
|
||||
)
|
||||
metadata.write_text(text)
|
||||
if sha256(payload) != payload_hash:
|
||||
raise RuntimeError("KTransformers payload changed while updating metadata")
|
||||
|
||||
write_record(root, new_dist_info)
|
||||
output = output_dir / (
|
||||
"ktransformers-0.7.0.post3-py3-none-manylinux_2_35_x86_64.whl"
|
||||
)
|
||||
pack_wheel(root, output)
|
||||
return output
|
||||
|
||||
|
||||
def require_hash(path: Path, expected: str) -> None:
|
||||
actual = sha256(path)
|
||||
if actual != expected:
|
||||
raise RuntimeError(f"Unexpected frozen wheel hash for {path}: {actual} != {expected}")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--post2-dir", required=True, type=Path)
|
||||
parser.add_argument("--sglang-source", required=True, type=Path)
|
||||
parser.add_argument("--output-dir", required=True, type=Path)
|
||||
parser.add_argument("--sglang-sha", required=True)
|
||||
parser.add_argument("--ktransformers-sha", required=True)
|
||||
args = parser.parse_args()
|
||||
args.output_dir.mkdir(parents=True, exist_ok=False)
|
||||
|
||||
inputs = {
|
||||
"kt_kernel": args.post2_dir
|
||||
/ "kt_kernel-0.7.0.post2-cp312-cp312-manylinux_2_35_x86_64.whl",
|
||||
"ktransformers": args.post2_dir
|
||||
/ "ktransformers-0.7.0.post2-py3-none-manylinux_2_35_x86_64.whl",
|
||||
"sglang": args.post2_dir
|
||||
/ "sglang_kt-0.7.0.post2-py3-none-manylinux_2_35_x86_64.whl",
|
||||
"transformers": args.post2_dir
|
||||
/ "transformers_kt-5.6.0.post4-py3-none-manylinux_2_35_x86_64.whl",
|
||||
}
|
||||
for name, path in inputs.items():
|
||||
require_hash(path, POST2_HASHES[name])
|
||||
|
||||
outputs = [
|
||||
build_sglang(inputs["sglang"], args.sglang_source, args.output_dir),
|
||||
build_ktransformers(inputs["ktransformers"], args.output_dir),
|
||||
]
|
||||
for name in ("kt_kernel", "transformers"):
|
||||
destination = args.output_dir / inputs[name].name
|
||||
shutil.copy2(inputs[name], destination)
|
||||
require_hash(destination, POST2_HASHES[name])
|
||||
outputs.append(destination)
|
||||
|
||||
manifest = {
|
||||
"source": {
|
||||
"sglang": args.sglang_sha,
|
||||
"ktransformers": args.ktransformers_sha,
|
||||
},
|
||||
"wheels": {path.name: sha256(path) for path in sorted(outputs)},
|
||||
}
|
||||
(args.output_dir / "release-manifest.json").write_text(
|
||||
json.dumps(manifest, indent=2, sort_keys=True) + "\n"
|
||||
)
|
||||
(args.output_dir / "SHA256SUMS").write_text(
|
||||
"".join(f"{sha256(path)} {path.name}\n" for path in sorted(outputs))
|
||||
)
|
||||
print(json.dumps(manifest, indent=2, sort_keys=True))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user