fix delta corruption. removed dead code. added delta for --multi tensor cast.
Browse files
voice.py
CHANGED
|
@@ -1929,6 +1929,15 @@ def cmd_cast(args):
|
|
| 1929 |
target_format = "gguf" if _is_gguf_file(target) else "safetensors"
|
| 1930 |
out_path = _out_path(args, target, voice_arg)
|
| 1931 |
_check_overwrite(out_path, args)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1932 |
|
| 1933 |
# auto-detect delta file via marker even without voice.json (e.g. shipped style.safetensors)
|
| 1934 |
# do this after _check_overwrite so prompt and Detected don't interleave
|
|
@@ -2161,63 +2170,11 @@ def _quantize_to_Q(fused, req_q):
|
|
| 2161 |
return quantize(fused, req_q), req_q
|
| 2162 |
|
| 2163 |
|
| 2164 |
-
def
|
|
|
|
| 2165 |
import numpy as np
|
| 2166 |
-
from gguf import GGUFReader, GGUFWriter
|
| 2167 |
-
from gguf.gguf_writer import GGUFValue
|
| 2168 |
from gguf.constants import GGMLQuantizationType as Q
|
| 2169 |
from gguf.quants import quantize
|
| 2170 |
-
|
| 2171 |
-
reader = GGUFReader(target)
|
| 2172 |
-
arch = None
|
| 2173 |
-
for field in reader.fields.values():
|
| 2174 |
-
if field.name == "general.architecture":
|
| 2175 |
-
try:
|
| 2176 |
-
raw = field.parts[field.data[0]]
|
| 2177 |
-
arch = raw.tobytes().decode() if isinstance(raw, np.ndarray) else str(raw)
|
| 2178 |
-
except Exception:
|
| 2179 |
-
arch = None
|
| 2180 |
-
break
|
| 2181 |
-
|
| 2182 |
-
target_tensor = None
|
| 2183 |
-
if vref.name:
|
| 2184 |
-
for t in reader.tensors:
|
| 2185 |
-
if t.name == vref.name:
|
| 2186 |
-
target_tensor = t
|
| 2187 |
-
break
|
| 2188 |
-
if target_tensor is None and strict_target:
|
| 2189 |
-
_fail(f" β Tensor '{vref.name}' not found in target {target}. Voicepack requires exact match (not mapping to another tensor).")
|
| 2190 |
-
if target_tensor is None:
|
| 2191 |
-
for preferred in ("output.weight", "token_embd.weight"):
|
| 2192 |
-
for t in reader.tensors:
|
| 2193 |
-
if t.name == preferred:
|
| 2194 |
-
target_tensor = t
|
| 2195 |
-
break
|
| 2196 |
-
if target_tensor is not None:
|
| 2197 |
-
break
|
| 2198 |
-
if target_tensor is None:
|
| 2199 |
-
# largest 2-D
|
| 2200 |
-
best, best_n = None, 0
|
| 2201 |
-
for t in reader.tensors:
|
| 2202 |
-
if len(t.shape) == 2:
|
| 2203 |
-
n = int(t.shape[0]) * int(t.shape[1])
|
| 2204 |
-
if n > best_n:
|
| 2205 |
-
best, best_n = t, n
|
| 2206 |
-
target_tensor = best
|
| 2207 |
-
if target_tensor is None:
|
| 2208 |
-
_fail(" β Could not find an output tensor in the GGUF file.")
|
| 2209 |
-
|
| 2210 |
-
tname = target_tensor.name
|
| 2211 |
-
logical = tuple(reversed(tuple(int(x) for x in target_tensor.shape)))
|
| 2212 |
-
|
| 2213 |
-
# arch mismatch warning
|
| 2214 |
-
varch = (meta or {}).get("architecture")
|
| 2215 |
-
if varch and arch and varch.lower() != arch.lower():
|
| 2216 |
-
_warn(f"This voice was trained for a {varch} model, but the target is {arch}. It may not sound right.")
|
| 2217 |
-
if not _confirm(" Continue anyway? [y/N]: ", args):
|
| 2218 |
-
_note("Cancelled.")
|
| 2219 |
-
sys.exit(0)
|
| 2220 |
-
|
| 2221 |
# Philosophy: preserve quality, avoid dequant->quant cycles.
|
| 2222 |
# - GGUF quant voice (vraw) is kept at its obtained quant (never upscale Q2->Q6, no inventing data).
|
| 2223 |
# - Safetensors high-fidelity lm_head (voice) is quantized once to Q8_0.
|
|
@@ -2527,7 +2484,14 @@ def _cast_gguf(target, out_path, vf32, vref, meta, args, op, vraw=None, vqtype=N
|
|
| 2527 |
except Exception:
|
| 2528 |
t_fuse0 = time.time()
|
| 2529 |
_note(f"Fusing done in {time.time()-t_fuse0:.1f}s")
|
|
|
|
| 2530 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2531 |
if not arch:
|
| 2532 |
_warn(" Target architecture unknown β labeling output 'llama' (GGUF requires an arch).")
|
| 2533 |
writer = GGUFWriter(out_path, arch or "llama", use_temp_file=True)
|
|
@@ -2552,8 +2516,9 @@ def _cast_gguf(target, out_path, vf32, vref, meta, args, op, vraw=None, vqtype=N
|
|
| 2552 |
_warn(f"Could not copy field {field.name}")
|
| 2553 |
|
| 2554 |
for t in reader.tensors:
|
| 2555 |
-
if t.name
|
| 2556 |
-
|
|
|
|
| 2557 |
elif t.tensor_type in (Q.F32, Q.F16):
|
| 2558 |
writer.add_tensor(t.name, t.data)
|
| 2559 |
else:
|
|
@@ -2565,7 +2530,7 @@ def _cast_gguf(target, out_path, vf32, vref, meta, args, op, vraw=None, vqtype=N
|
|
| 2565 |
try:
|
| 2566 |
total_write = 8192
|
| 2567 |
for t in reader.tensors:
|
| 2568 |
-
if t.name
|
| 2569 |
continue
|
| 2570 |
try:
|
| 2571 |
total_write += int(t.data.nbytes)
|
|
@@ -2573,26 +2538,31 @@ def _cast_gguf(target, out_path, vf32, vref, meta, args, op, vraw=None, vqtype=N
|
|
| 2573 |
if VERBOSE:
|
| 2574 |
_warn(f"progress estimate for {t.name} failed: {e}")
|
| 2575 |
try:
|
| 2576 |
-
|
| 2577 |
-
|
| 2578 |
-
|
| 2579 |
-
_warn(f"progress estimate for q_head failed: {e}")
|
| 2580 |
try:
|
| 2581 |
-
|
|
|
|
| 2582 |
except Exception:
|
| 2583 |
pass
|
| 2584 |
if VERBOSE:
|
| 2585 |
-
_note(f"Estimated write {total_write/1e6:.1f} MB (
|
| 2586 |
except Exception as e:
|
| 2587 |
if VERBOSE:
|
| 2588 |
_warn(f"progress estimate failed: {e}")
|
| 2589 |
total_write = 0
|
| 2590 |
-
# ensure total_write is at least header +
|
| 2591 |
if total_write < 1000:
|
| 2592 |
try:
|
| 2593 |
-
total_write =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2594 |
for t in reader.tensors:
|
| 2595 |
-
if t.name
|
| 2596 |
try:
|
| 2597 |
total_write += int(t.data.nbytes)
|
| 2598 |
except Exception:
|
|
@@ -2673,6 +2643,17 @@ def _cast_gguf(target, out_path, vf32, vref, meta, args, op, vraw=None, vqtype=N
|
|
| 2673 |
pass
|
| 2674 |
|
| 2675 |
file_size = os.path.getsize(out_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2676 |
log_operation({"op": op, "target": target, "format": "gguf", "tensor": tname,
|
| 2677 |
"dtype": out_qtype.name, "shape": list(logical), "output": out_path, "status": "ok"})
|
| 2678 |
_ok(f"Done β {out_path}")
|
|
@@ -2690,7 +2671,8 @@ def _cast_gguf(target, out_path, vf32, vref, meta, args, op, vraw=None, vqtype=N
|
|
| 2690 |
elif is_fallback:
|
| 2691 |
_say(f" Head kept as F16 (Q8_0 needs last dim %32, fallback) ({file_size/1e6:.1f} MB).")
|
| 2692 |
elif out_qtype == Q.Q8_0 and _is_head_msg:
|
| 2693 |
-
|
|
|
|
| 2694 |
if _kept_q8:
|
| 2695 |
_say(f" Head kept as Q8_0 (from {vqtype}, {file_size/1e6:.1f} MB).")
|
| 2696 |
else:
|
|
@@ -2700,6 +2682,154 @@ def _cast_gguf(target, out_path, vf32, vref, meta, args, op, vraw=None, vqtype=N
|
|
| 2700 |
_say(f" {_label} '{tname}' kept as {out_qtype.name} (from {_from}, {file_size/1e6:.1f} MB).")
|
| 2701 |
_say(f" Load it: llama serve -m {out_path}")
|
| 2702 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2703 |
|
| 2704 |
def _write_fetched(fetched, tensor_name, out_path):
|
| 2705 |
"""Write a fetched tensor to a standalone artifact (gguf when quantized)."""
|
|
@@ -2891,6 +3021,17 @@ def cmd_tensor_get(args):
|
|
| 2891 |
# CMD: tensor cast β cast any named tensor onto a file
|
| 2892 |
# ---------------------------------------------------------------------------
|
| 2893 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2894 |
def cmd_tensor_cast(args):
|
| 2895 |
target = args.target
|
| 2896 |
if not os.path.exists(target):
|
|
@@ -2917,52 +3058,13 @@ def cmd_tensor_cast(args):
|
|
| 2917 |
target_format = "gguf" if _is_gguf_file(target) else "safetensors"
|
| 2918 |
out_path = _out_path(args, target, src_path)
|
| 2919 |
_check_overwrite(out_path, args)
|
| 2920 |
-
_cast_core(target, target_format, out_path, f32, ref,
|
| 2921 |
return
|
| 2922 |
if len(_all_names) > 1:
|
| 2923 |
-
# Voicepack
|
| 2924 |
-
# Validate excludes exist (warn on unknown, fail on all-excluded).
|
| 2925 |
-
for e in excludes:
|
| 2926 |
-
if e not in _all_names and not any(e in n for n in _all_names):
|
| 2927 |
-
_warn(f" --exclude '{e}' not in pack (has {len(_all_names)} tensors). Ignoring.")
|
| 2928 |
-
# Resolve excludes via substring single-match like get (exact first).
|
| 2929 |
-
_resolved_ex = set()
|
| 2930 |
-
for e in excludes:
|
| 2931 |
-
if e in _all_names:
|
| 2932 |
-
_resolved_ex.add(e)
|
| 2933 |
-
else:
|
| 2934 |
-
_m = [n for n in _all_names if e in n]
|
| 2935 |
-
if len(_m) == 1:
|
| 2936 |
-
_resolved_ex.add(_m[0])
|
| 2937 |
-
to_cast = [n for n in _all_names if n not in _resolved_ex]
|
| 2938 |
-
if not to_cast:
|
| 2939 |
-
_fail(" β --exclude removes all tensors. Nothing to cast.")
|
| 2940 |
-
_step(f"Casting {len(to_cast)} tensors from {Path(src_path).name} onto {Path(target).name}β¦")
|
| 2941 |
-
if excludes:
|
| 2942 |
-
_note(f"Excluding {len(_resolved_ex)}: {', '.join(sorted(_resolved_ex))}")
|
| 2943 |
-
target_format = "gguf" if _is_gguf_file(target) else "safetensors"
|
| 2944 |
out_path = _out_path(args, target, src_path)
|
| 2945 |
_check_overwrite(out_path, args)
|
| 2946 |
-
|
| 2947 |
-
# For correctness with philosophy (keep target quant per tensor), reuse _cast_core per tensor.
|
| 2948 |
-
import tempfile, shutil
|
| 2949 |
-
_cur_in = target
|
| 2950 |
-
_tmpdir = Path(tempfile.mkdtemp(prefix="voicepack-cast-"))
|
| 2951 |
-
try:
|
| 2952 |
-
for i, tname in enumerate(to_cast):
|
| 2953 |
-
is_last = (i == len(to_cast) - 1)
|
| 2954 |
-
_out = out_path if is_last else str(_tmpdir / f"step{i}.tmp")
|
| 2955 |
-
f32, ref, raw, qtype = _load_tensor(src_path, tname)
|
| 2956 |
-
_step(f"[{i+1}/{len(to_cast)}] {tname} ({ref.dtype} {tuple(ref.shape)})β¦")
|
| 2957 |
-
_cast_core(_cur_in, "gguf" if _is_gguf_file(_cur_in) else "safetensors",
|
| 2958 |
-
_out, f32, ref, None, args, op="tensor_cast", vraw=raw, vqtype=qtype,
|
| 2959 |
-
strict_target=True)
|
| 2960 |
-
_cur_in = _out
|
| 2961 |
-
finally:
|
| 2962 |
-
try:
|
| 2963 |
-
shutil.rmtree(_tmpdir, ignore_errors=True)
|
| 2964 |
-
except Exception:
|
| 2965 |
-
pass
|
| 2966 |
return
|
| 2967 |
# Single-tensor pack (or single file) without --tensor flag: original behavior.
|
| 2968 |
f32, ref, raw, qtype = _load_tensor(src_path, None)
|
|
@@ -2971,7 +3073,7 @@ def cmd_tensor_cast(args):
|
|
| 2971 |
target_format = "gguf" if _is_gguf_file(target) else "safetensors"
|
| 2972 |
out_path = _out_path(args, target, src_path)
|
| 2973 |
_check_overwrite(out_path, args)
|
| 2974 |
-
_cast_core(target, target_format, out_path, f32, ref,
|
| 2975 |
|
| 2976 |
|
| 2977 |
def _load_tensor(path, name=None):
|
|
@@ -3167,14 +3269,18 @@ def cmd_delta(args):
|
|
| 3167 |
"dtype": "F16",
|
| 3168 |
"downloaded_at": _now_iso(),
|
| 3169 |
}
|
| 3170 |
-
# enrich with shape/dtype from the delta file
|
| 3171 |
try:
|
| 3172 |
hdr, tens = read_safetensors(str(delta_folder / "voice.safetensors"))
|
| 3173 |
-
|
| 3174 |
-
meta["
|
| 3175 |
-
|
| 3176 |
-
|
| 3177 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3178 |
except Exception:
|
| 3179 |
pass
|
| 3180 |
jtmp = delta_folder / "voice.json.tmp"
|
|
@@ -3381,20 +3487,19 @@ def _compute_delta(voice_path, base_path, delta_folder, delta_name, args=None):
|
|
| 3381 |
b_names = [n for n in b_src.names() if n != "delta.voice.marker"]
|
| 3382 |
if not v_names or not b_names:
|
| 3383 |
_fail(" β No tensors found for delta.")
|
| 3384 |
-
#
|
| 3385 |
-
|
| 3386 |
-
# Try to match voice name in base.
|
| 3387 |
try:
|
| 3388 |
arch, cfg = _source_arch(v_src)
|
| 3389 |
_cand = _resolve_output_name(v_src, cfg, v_names)
|
| 3390 |
-
if _cand in v_names:
|
| 3391 |
-
vname = _cand
|
| 3392 |
except Exception:
|
| 3393 |
-
|
| 3394 |
-
|
| 3395 |
-
|
| 3396 |
-
if
|
| 3397 |
-
|
|
|
|
|
|
|
| 3398 |
# Gates (marker check works for both safetensors header and GGUF? GGUF has no marker, so False unless safetensors delta).
|
| 3399 |
is_voice_delta = _is_delta_safetensors(str(voice_path)) if str(voice_path).endswith(".safetensors") else False
|
| 3400 |
is_base_delta = _is_delta_safetensors(str(base_path)) if str(base_path).endswith(".safetensors") else False
|
|
@@ -3413,11 +3518,6 @@ def _compute_delta(voice_path, base_path, delta_folder, delta_name, args=None):
|
|
| 3413 |
" The voice and base are both delta-voices.",
|
| 3414 |
" delta β delta = style difference of style differences."],
|
| 3415 |
"BIG BRAIN", "delta-on-delta needs explicit confirmation")
|
| 3416 |
-
_warn(f" Dequanting {vname} ({vref.dtype}) and {bname} ({bref.dtype}) once to F32 for delta math (unavoidable for GGUF quant).")
|
| 3417 |
-
v_f32, b_f32 = v_src.read_f32(vname), b_src.read_f32(bname)
|
| 3418 |
-
if v_f32.shape != b_f32.shape:
|
| 3419 |
-
_fail(f" β Shape mismatch after dequant: {v_f32.shape} vs {b_f32.shape}.")
|
| 3420 |
-
delta_f16 = (v_f32.astype(np.float32) - b_f32.astype(np.float32)).astype("float16")
|
| 3421 |
_assert_not_symlink(delta_folder)
|
| 3422 |
delta_folder.mkdir(parents=True, exist_ok=True)
|
| 3423 |
try:
|
|
@@ -3426,17 +3526,32 @@ def _compute_delta(voice_path, base_path, delta_folder, delta_name, args=None):
|
|
| 3426 |
pass
|
| 3427 |
out_path = delta_folder / "voice.safetensors"
|
| 3428 |
tmp_out = delta_folder / "voice.safetensors.tmp"
|
| 3429 |
-
|
| 3430 |
-
|
| 3431 |
-
|
| 3432 |
-
|
| 3433 |
-
|
| 3434 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3435 |
hj = json.dumps(header).encode("utf-8")
|
| 3436 |
with open(tmp_out, "wb") as out:
|
| 3437 |
out.write(struct.pack("<Q", len(hj)))
|
| 3438 |
out.write(hj)
|
| 3439 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3440 |
out.write(b"\x01")
|
| 3441 |
out.flush()
|
| 3442 |
try:
|
|
@@ -3446,13 +3561,15 @@ def _compute_delta(voice_path, base_path, delta_folder, delta_name, args=None):
|
|
| 3446 |
tmp_out.replace(out_path)
|
| 3447 |
return out_path
|
| 3448 |
# Original chunked safetensors-safetensors path below (no dequant beyond F32 math per chunk).
|
| 3449 |
-
#
|
| 3450 |
v_keys = [k for k in v_tensors.keys() if k != "delta.voice.marker"]
|
| 3451 |
-
|
| 3452 |
-
|
| 3453 |
-
|
| 3454 |
-
|
| 3455 |
-
|
|
|
|
|
|
|
| 3456 |
# gates for delta-on-delta niche cases (marker indicates delta file)
|
| 3457 |
is_voice_delta = _is_delta_safetensors(str(voice_path))
|
| 3458 |
is_base_delta = _is_delta_safetensors(str(base_path))
|
|
@@ -3470,21 +3587,33 @@ def _compute_delta(voice_path, base_path, delta_folder, delta_name, args=None):
|
|
| 3470 |
" The voice and base are both delta-voices.",
|
| 3471 |
" delta β delta = style difference of style differences."],
|
| 3472 |
"BIG BRAIN", "delta-on-delta needs explicit confirmation")
|
| 3473 |
-
if vshape != bshape:
|
| 3474 |
-
_fail(f" β Voice shape {vshape} doesn't fit base {bshape}.")
|
| 3475 |
-
total_elems = 1
|
| 3476 |
-
for d in vshape:
|
| 3477 |
-
total_elems *= int(d)
|
| 3478 |
-
|
| 3479 |
with open(voice_path, "rb") as f:
|
| 3480 |
v_hs = struct.unpack("<Q", f.read(8))[0]
|
| 3481 |
with open(base_path, "rb") as f:
|
| 3482 |
b_hs = struct.unpack("<Q", f.read(8))[0]
|
| 3483 |
-
|
| 3484 |
-
|
| 3485 |
-
|
| 3486 |
-
|
| 3487 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3488 |
_assert_not_symlink(delta_folder)
|
| 3489 |
delta_folder.mkdir(parents=True, exist_ok=True)
|
| 3490 |
try:
|
|
@@ -3493,14 +3622,6 @@ def _compute_delta(voice_path, base_path, delta_folder, delta_name, args=None):
|
|
| 3493 |
pass
|
| 3494 |
out_path = delta_folder / "voice.safetensors"
|
| 3495 |
tmp_out = delta_folder / "voice.safetensors.tmp"
|
| 3496 |
-
marker_name = "delta.voice.marker"
|
| 3497 |
-
header = {
|
| 3498 |
-
vname: {"dtype": "F16", "shape": list(vshape), "data_offsets": [0, total_elems * 2]},
|
| 3499 |
-
marker_name: {"dtype": "U8", "shape": [1], "data_offsets": [total_elems * 2, total_elems * 2 + 1]},
|
| 3500 |
-
"__metadata__": {"type": "delta", "base": _public_source(str(base_path)), "voice": _public_source(str(voice_path))},
|
| 3501 |
-
}
|
| 3502 |
-
hj = json.dumps(header).encode("utf-8")
|
| 3503 |
-
|
| 3504 |
chunk = 1 << 20
|
| 3505 |
total_bytes = total_elems * 2
|
| 3506 |
t0 = time.time()
|
|
@@ -3521,23 +3642,28 @@ def _compute_delta(voice_path, base_path, delta_folder, delta_name, args=None):
|
|
| 3521 |
with open(voice_path, "rb") as fv, open(base_path, "rb") as fb, open(tmp_out, "wb") as out:
|
| 3522 |
out.write(struct.pack("<Q", len(hj)))
|
| 3523 |
out.write(hj)
|
| 3524 |
-
fv.seek(v_start)
|
| 3525 |
-
fb.seek(b_start)
|
| 3526 |
processed = 0
|
| 3527 |
_delta_progress(force=True)
|
| 3528 |
-
|
| 3529 |
-
|
| 3530 |
-
|
| 3531 |
-
|
| 3532 |
-
|
| 3533 |
-
|
| 3534 |
-
|
| 3535 |
-
|
| 3536 |
-
|
| 3537 |
-
|
| 3538 |
-
|
| 3539 |
-
|
| 3540 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3541 |
_delta_progress(force=True)
|
| 3542 |
sys.stdout.write("\n")
|
| 3543 |
# marker byte for auto-detect (delta.voice.marker)
|
|
@@ -3782,6 +3908,33 @@ def _verify_voice_file(path, meta):
|
|
| 3782 |
except Exception:
|
| 3783 |
return [" ? Could not read the artifact header; showing voice.json as-is."]
|
| 3784 |
names = [k for k in tens if k != "delta.voice.marker"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3785 |
tname = meta.get("tensor_name")
|
| 3786 |
if tname not in tens and names:
|
| 3787 |
tname = names[0]
|
|
@@ -3834,6 +3987,9 @@ def cmd_info(args):
|
|
| 3834 |
_say(f" Tensor: {meta.get('tensor_name', 'β')}")
|
| 3835 |
_say(f" Shape: {meta.get('shape', 'β')}")
|
| 3836 |
_say(f" Dtype: {meta.get('dtype', 'β')}")
|
|
|
|
|
|
|
|
|
|
| 3837 |
b = meta.get("bytes")
|
| 3838 |
if isinstance(b, int):
|
| 3839 |
_say(f" Bytes: {b} ({b/1e6:.1f} MB)")
|
|
|
|
| 1929 |
target_format = "gguf" if _is_gguf_file(target) else "safetensors"
|
| 1930 |
out_path = _out_path(args, target, voice_arg)
|
| 1931 |
_check_overwrite(out_path, args)
|
| 1932 |
+
# Voicepack in the registry (or a pack path): cast every tensor, not just the first.
|
| 1933 |
+
try:
|
| 1934 |
+
_probe = open_source(str(voice_path))
|
| 1935 |
+
_pack_names = [n for n in _probe.names() if n != "delta.voice.marker"]
|
| 1936 |
+
except Exception:
|
| 1937 |
+
_pack_names = []
|
| 1938 |
+
if len(_pack_names) > 1:
|
| 1939 |
+
_cast_pack(target, str(voice_path), out_path, args, op="cast", meta=voice_meta)
|
| 1940 |
+
return
|
| 1941 |
|
| 1942 |
# auto-detect delta file via marker even without voice.json (e.g. shipped style.safetensors)
|
| 1943 |
# do this after _check_overwrite so prompt and Detected don't interleave
|
|
|
|
| 2170 |
return quantize(fused, req_q), req_q
|
| 2171 |
|
| 2172 |
|
| 2173 |
+
def _gguf_fuse_one(target, target_tensor, tname, logical, vf32, vref, meta, args, op, vraw=None, vqtype=None):
|
| 2174 |
+
"""Fuse one voice tensor into its target GGUF tensor form. Returns (q_head, out_qtype)."""
|
| 2175 |
import numpy as np
|
|
|
|
|
|
|
| 2176 |
from gguf.constants import GGMLQuantizationType as Q
|
| 2177 |
from gguf.quants import quantize
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2178 |
# Philosophy: preserve quality, avoid dequant->quant cycles.
|
| 2179 |
# - GGUF quant voice (vraw) is kept at its obtained quant (never upscale Q2->Q6, no inventing data).
|
| 2180 |
# - Safetensors high-fidelity lm_head (voice) is quantized once to Q8_0.
|
|
|
|
| 2484 |
except Exception:
|
| 2485 |
t_fuse0 = time.time()
|
| 2486 |
_note(f"Fusing done in {time.time()-t_fuse0:.1f}s")
|
| 2487 |
+
return q_head, out_qtype
|
| 2488 |
|
| 2489 |
+
def _gguf_write_repl(target, reader, arch, repl, out_path, args, op, detail=None):
|
| 2490 |
+
"""Single-pass GGUF write. repl maps tensor name -> (array, ggml_type).
|
| 2491 |
+
detail (single-tensor callers): (tname, logical, vref, vraw, vqtype, meta) for rich messages."""
|
| 2492 |
+
from gguf import GGUFWriter
|
| 2493 |
+
from gguf.gguf_writer import GGUFValue
|
| 2494 |
+
from gguf.constants import GGMLQuantizationType as Q
|
| 2495 |
if not arch:
|
| 2496 |
_warn(" Target architecture unknown β labeling output 'llama' (GGUF requires an arch).")
|
| 2497 |
writer = GGUFWriter(out_path, arch or "llama", use_temp_file=True)
|
|
|
|
| 2516 |
_warn(f"Could not copy field {field.name}")
|
| 2517 |
|
| 2518 |
for t in reader.tensors:
|
| 2519 |
+
if t.name in repl:
|
| 2520 |
+
_qh, _qt = repl[t.name]
|
| 2521 |
+
writer.add_tensor(t.name, _qh, raw_dtype=_qt)
|
| 2522 |
elif t.tensor_type in (Q.F32, Q.F16):
|
| 2523 |
writer.add_tensor(t.name, t.data)
|
| 2524 |
else:
|
|
|
|
| 2530 |
try:
|
| 2531 |
total_write = 8192
|
| 2532 |
for t in reader.tensors:
|
| 2533 |
+
if t.name in repl:
|
| 2534 |
continue
|
| 2535 |
try:
|
| 2536 |
total_write += int(t.data.nbytes)
|
|
|
|
| 2538 |
if VERBOSE:
|
| 2539 |
_warn(f"progress estimate for {t.name} failed: {e}")
|
| 2540 |
try:
|
| 2541 |
+
for _qh, _qt in repl.values():
|
| 2542 |
+
total_write += int(_qh.nbytes)
|
| 2543 |
+
except Exception:
|
|
|
|
| 2544 |
try:
|
| 2545 |
+
for _qh, _qt in repl.values():
|
| 2546 |
+
total_write += int(_qh.size * _qh.itemsize)
|
| 2547 |
except Exception:
|
| 2548 |
pass
|
| 2549 |
if VERBOSE:
|
| 2550 |
+
_note(f"Estimated write {total_write/1e6:.1f} MB ({len(repl)} replaced + {len([t for t in reader.tensors if t.name not in repl])} copied)")
|
| 2551 |
except Exception as e:
|
| 2552 |
if VERBOSE:
|
| 2553 |
_warn(f"progress estimate failed: {e}")
|
| 2554 |
total_write = 0
|
| 2555 |
+
# ensure total_write is at least header + heads for small models, never 0 for progress
|
| 2556 |
if total_write < 1000:
|
| 2557 |
try:
|
| 2558 |
+
total_write = 8192
|
| 2559 |
+
for _qh, _qt in repl.values():
|
| 2560 |
+
try:
|
| 2561 |
+
total_write += int(_qh.nbytes)
|
| 2562 |
+
except Exception:
|
| 2563 |
+
pass
|
| 2564 |
for t in reader.tensors:
|
| 2565 |
+
if t.name not in repl:
|
| 2566 |
try:
|
| 2567 |
total_write += int(t.data.nbytes)
|
| 2568 |
except Exception:
|
|
|
|
| 2643 |
pass
|
| 2644 |
|
| 2645 |
file_size = os.path.getsize(out_path)
|
| 2646 |
+
if detail is None:
|
| 2647 |
+
summed = [(n, _qt.name if hasattr(_qt, "name") else str(_qt)) for n, (_qh, _qt) in repl.items()]
|
| 2648 |
+
log_operation({"op": op, "target": target, "format": "gguf", "tensors": summed,
|
| 2649 |
+
"output": out_path, "status": "ok"})
|
| 2650 |
+
_ok(f"Done β {out_path}")
|
| 2651 |
+
for n, _qt in summed:
|
| 2652 |
+
_say(f" Cast '{n}' β {_qt} ({file_size/1e6:.1f} MB total).")
|
| 2653 |
+
_say(f" Load it: llama serve -m {out_path}")
|
| 2654 |
+
return
|
| 2655 |
+
tname, logical, vref, vraw, vqtype, meta = detail
|
| 2656 |
+
_qh0, out_qtype = repl[tname]
|
| 2657 |
log_operation({"op": op, "target": target, "format": "gguf", "tensor": tname,
|
| 2658 |
"dtype": out_qtype.name, "shape": list(logical), "output": out_path, "status": "ok"})
|
| 2659 |
_ok(f"Done β {out_path}")
|
|
|
|
| 2671 |
elif is_fallback:
|
| 2672 |
_say(f" Head kept as F16 (Q8_0 needs last dim %32, fallback) ({file_size/1e6:.1f} MB).")
|
| 2673 |
elif out_qtype == Q.Q8_0 and _is_head_msg:
|
| 2674 |
+
_qo = _parse_quant_override(getattr(args, "quant", None) or "keep")
|
| 2675 |
+
_kept_q8 = (vraw is not None and isinstance(vqtype, str) and vqtype == Q.Q8_0.name and _qo in ("keep",))
|
| 2676 |
if _kept_q8:
|
| 2677 |
_say(f" Head kept as Q8_0 (from {vqtype}, {file_size/1e6:.1f} MB).")
|
| 2678 |
else:
|
|
|
|
| 2682 |
_say(f" {_label} '{tname}' kept as {out_qtype.name} (from {_from}, {file_size/1e6:.1f} MB).")
|
| 2683 |
_say(f" Load it: llama serve -m {out_path}")
|
| 2684 |
|
| 2685 |
+
def _cast_gguf(target, out_path, vf32, vref, meta, args, op, vraw=None, vqtype=None, strict_target=False):
|
| 2686 |
+
import numpy as np
|
| 2687 |
+
from gguf import GGUFReader
|
| 2688 |
+
|
| 2689 |
+
reader = GGUFReader(target)
|
| 2690 |
+
arch = None
|
| 2691 |
+
for field in reader.fields.values():
|
| 2692 |
+
if field.name == "general.architecture":
|
| 2693 |
+
try:
|
| 2694 |
+
raw = field.parts[field.data[0]]
|
| 2695 |
+
arch = raw.tobytes().decode() if isinstance(raw, np.ndarray) else str(raw)
|
| 2696 |
+
except Exception:
|
| 2697 |
+
arch = None
|
| 2698 |
+
break
|
| 2699 |
+
|
| 2700 |
+
target_tensor = None
|
| 2701 |
+
if vref.name:
|
| 2702 |
+
for t in reader.tensors:
|
| 2703 |
+
if t.name == vref.name:
|
| 2704 |
+
target_tensor = t
|
| 2705 |
+
break
|
| 2706 |
+
if target_tensor is None and strict_target:
|
| 2707 |
+
_fail(f" β Tensor '{vref.name}' not found in target {target}. Voicepack requires exact match (not mapping to another tensor).")
|
| 2708 |
+
if target_tensor is None:
|
| 2709 |
+
for preferred in ("output.weight", "token_embd.weight"):
|
| 2710 |
+
for t in reader.tensors:
|
| 2711 |
+
if t.name == preferred:
|
| 2712 |
+
target_tensor = t
|
| 2713 |
+
break
|
| 2714 |
+
if target_tensor is not None:
|
| 2715 |
+
break
|
| 2716 |
+
if target_tensor is None:
|
| 2717 |
+
# largest 2-D
|
| 2718 |
+
best, best_n = None, 0
|
| 2719 |
+
for t in reader.tensors:
|
| 2720 |
+
if len(t.shape) == 2:
|
| 2721 |
+
n = int(t.shape[0]) * int(t.shape[1])
|
| 2722 |
+
if n > best_n:
|
| 2723 |
+
best, best_n = t, n
|
| 2724 |
+
target_tensor = best
|
| 2725 |
+
if target_tensor is None:
|
| 2726 |
+
_fail(" β Could not find an output tensor in the GGUF file.")
|
| 2727 |
+
|
| 2728 |
+
tname = target_tensor.name
|
| 2729 |
+
logical = tuple(reversed(tuple(int(x) for x in target_tensor.shape)))
|
| 2730 |
+
|
| 2731 |
+
# arch mismatch warning
|
| 2732 |
+
varch = (meta or {}).get("architecture")
|
| 2733 |
+
if varch and arch and varch.lower() != arch.lower():
|
| 2734 |
+
_warn(f"This voice was trained for a {varch} model, but the target is {arch}. It may not sound right.")
|
| 2735 |
+
if not _confirm(" Continue anyway? [y/N]: ", args):
|
| 2736 |
+
_note("Cancelled.")
|
| 2737 |
+
sys.exit(0)
|
| 2738 |
+
|
| 2739 |
+
q_head, out_qtype = _gguf_fuse_one(target, target_tensor, tname, logical, vf32, vref, meta, args, op, vraw=vraw, vqtype=vqtype)
|
| 2740 |
+
_gguf_write_repl(target, reader, arch, {tname: (q_head, out_qtype)}, out_path, args, op,
|
| 2741 |
+
detail=(tname, logical, vref, vraw, vqtype, meta))
|
| 2742 |
+
|
| 2743 |
+
|
| 2744 |
+
|
| 2745 |
+
def _cast_gguf_pack(target, out_path, items, args, op):
|
| 2746 |
+
"""Single-pass pack cast onto GGUF. items: [(vf32, vref, meta, vraw, vqtype)]."""
|
| 2747 |
+
import numpy as np
|
| 2748 |
+
from gguf import GGUFReader
|
| 2749 |
+
reader = GGUFReader(target)
|
| 2750 |
+
arch = None
|
| 2751 |
+
for field in reader.fields.values():
|
| 2752 |
+
if field.name == "general.architecture":
|
| 2753 |
+
try:
|
| 2754 |
+
raw = field.parts[field.data[0]]
|
| 2755 |
+
arch = raw.tobytes().decode() if isinstance(raw, np.ndarray) else str(raw)
|
| 2756 |
+
except Exception:
|
| 2757 |
+
arch = None
|
| 2758 |
+
break
|
| 2759 |
+
varch = (items[0][2] or {}).get("architecture") if items else None
|
| 2760 |
+
if varch and arch and varch.lower() != arch.lower():
|
| 2761 |
+
_warn(f"This voice was trained for a {varch} model, but the target is {arch}. It may not sound right.")
|
| 2762 |
+
if not _confirm(" Continue anyway? [y/N]: ", args):
|
| 2763 |
+
_note("Cancelled.")
|
| 2764 |
+
sys.exit(0)
|
| 2765 |
+
repl = {}
|
| 2766 |
+
for vf32, vref, meta, vraw, vqtype in items:
|
| 2767 |
+
target_tensor = None
|
| 2768 |
+
if vref.name:
|
| 2769 |
+
for t in reader.tensors:
|
| 2770 |
+
if t.name == vref.name:
|
| 2771 |
+
target_tensor = t
|
| 2772 |
+
break
|
| 2773 |
+
if target_tensor is None:
|
| 2774 |
+
_fail(f" β Tensor '{vref.name}' not found in target {target}. Voicepack requires exact match (not mapping to another tensor).")
|
| 2775 |
+
tname = target_tensor.name
|
| 2776 |
+
logical = tuple(reversed(tuple(int(x) for x in target_tensor.shape)))
|
| 2777 |
+
q_head, out_qtype = _gguf_fuse_one(target, target_tensor, tname, logical, vf32, vref, meta, args, op, vraw=vraw, vqtype=vqtype)
|
| 2778 |
+
repl[tname] = (q_head, out_qtype)
|
| 2779 |
+
_gguf_write_repl(target, reader, arch, repl, out_path, args, op)
|
| 2780 |
+
|
| 2781 |
+
|
| 2782 |
+
def _cast_pack(target, src_path, out_path, args, op, excludes=(), meta=None):
|
| 2783 |
+
"""Cast every tensor in a pack file onto target (minus excludes).
|
| 2784 |
+
GGUF targets get one write pass; safetensors targets chain per-tensor.
|
| 2785 |
+
Delta marker on the pack is detected once and threaded into every tensor."""
|
| 2786 |
+
src = open_source(str(src_path))
|
| 2787 |
+
all_names = [n for n in src.names() if n != "delta.voice.marker"]
|
| 2788 |
+
for e in excludes:
|
| 2789 |
+
if e not in all_names and not any(e in n for n in all_names):
|
| 2790 |
+
_warn(f" --exclude '{e}' not in pack (has {len(all_names)} tensors). Ignoring.")
|
| 2791 |
+
resolved_ex = set()
|
| 2792 |
+
for e in excludes:
|
| 2793 |
+
if e in all_names:
|
| 2794 |
+
resolved_ex.add(e)
|
| 2795 |
+
else:
|
| 2796 |
+
_m = [n for n in all_names if e in n]
|
| 2797 |
+
if len(_m) == 1:
|
| 2798 |
+
resolved_ex.add(_m[0])
|
| 2799 |
+
to_cast = [n for n in all_names if n not in resolved_ex]
|
| 2800 |
+
if not to_cast:
|
| 2801 |
+
_fail(" β --exclude removes all tensors. Nothing to cast.")
|
| 2802 |
+
_step(f"Casting {len(to_cast)} tensors from {Path(src_path).name} onto {Path(target).name}β¦")
|
| 2803 |
+
if resolved_ex:
|
| 2804 |
+
_note(f"Excluding {len(resolved_ex)}: {', '.join(sorted(resolved_ex))}")
|
| 2805 |
+
is_delta_file = str(src_path).endswith(".safetensors") and _is_delta_safetensors(str(src_path))
|
| 2806 |
+
if is_delta_file:
|
| 2807 |
+
_note("Detected delta voice β fusing style (delta + head)β¦")
|
| 2808 |
+
if meta is None and is_delta_file:
|
| 2809 |
+
meta = {"type": "delta", "source": str(src_path)}
|
| 2810 |
+
items = []
|
| 2811 |
+
for i, tname in enumerate(to_cast):
|
| 2812 |
+
f32, ref, raw, qtype = _load_tensor(src_path, tname)
|
| 2813 |
+
_step(f"[{i+1}/{len(to_cast)}] {tname} ({ref.dtype} {tuple(ref.shape)})β¦")
|
| 2814 |
+
items.append((f32, ref, meta, raw, qtype))
|
| 2815 |
+
if _is_gguf_file(target):
|
| 2816 |
+
_cast_gguf_pack(target, out_path, items, args, op)
|
| 2817 |
+
return
|
| 2818 |
+
import tempfile, shutil
|
| 2819 |
+
cur_in = target
|
| 2820 |
+
tmpdir = Path(tempfile.mkdtemp(prefix="voicepack-cast-"))
|
| 2821 |
+
try:
|
| 2822 |
+
for i, (f32, ref, meta, raw, qtype) in enumerate(items):
|
| 2823 |
+
is_last = (i == len(items) - 1)
|
| 2824 |
+
_out = out_path if is_last else str(tmpdir / f"step{i}.tmp")
|
| 2825 |
+
_cast_core(cur_in, "safetensors", _out, f32, ref, meta, args, op="tensor_cast",
|
| 2826 |
+
vraw=raw, vqtype=qtype, strict_target=True)
|
| 2827 |
+
cur_in = _out
|
| 2828 |
+
finally:
|
| 2829 |
+
try:
|
| 2830 |
+
shutil.rmtree(tmpdir, ignore_errors=True)
|
| 2831 |
+
except Exception:
|
| 2832 |
+
pass
|
| 2833 |
|
| 2834 |
def _write_fetched(fetched, tensor_name, out_path):
|
| 2835 |
"""Write a fetched tensor to a standalone artifact (gguf when quantized)."""
|
|
|
|
| 3021 |
# CMD: tensor cast β cast any named tensor onto a file
|
| 3022 |
# ---------------------------------------------------------------------------
|
| 3023 |
|
| 3024 |
+
def _tensor_src_meta(src_path):
|
| 3025 |
+
"""File-level delta detection for the tensor path (the marker lives in the file; no voice.json)."""
|
| 3026 |
+
if str(src_path).endswith(".safetensors"):
|
| 3027 |
+
try:
|
| 3028 |
+
if _is_delta_safetensors(str(src_path)):
|
| 3029 |
+
return {"type": "delta", "source": str(src_path)}
|
| 3030 |
+
except Exception:
|
| 3031 |
+
pass
|
| 3032 |
+
return None
|
| 3033 |
+
|
| 3034 |
+
|
| 3035 |
def cmd_tensor_cast(args):
|
| 3036 |
target = args.target
|
| 3037 |
if not os.path.exists(target):
|
|
|
|
| 3058 |
target_format = "gguf" if _is_gguf_file(target) else "safetensors"
|
| 3059 |
out_path = _out_path(args, target, src_path)
|
| 3060 |
_check_overwrite(out_path, args)
|
| 3061 |
+
_cast_core(target, target_format, out_path, f32, ref, _tensor_src_meta(src_path), args, op="tensor_cast", vraw=raw, vqtype=qtype)
|
| 3062 |
return
|
| 3063 |
if len(_all_names) > 1:
|
| 3064 |
+
# Voicepack: one pass for GGUF, chained for safetensors (see _cast_pack).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3065 |
out_path = _out_path(args, target, src_path)
|
| 3066 |
_check_overwrite(out_path, args)
|
| 3067 |
+
_cast_pack(target, src_path, out_path, args, op="tensor_cast", excludes=excludes)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3068 |
return
|
| 3069 |
# Single-tensor pack (or single file) without --tensor flag: original behavior.
|
| 3070 |
f32, ref, raw, qtype = _load_tensor(src_path, None)
|
|
|
|
| 3073 |
target_format = "gguf" if _is_gguf_file(target) else "safetensors"
|
| 3074 |
out_path = _out_path(args, target, src_path)
|
| 3075 |
_check_overwrite(out_path, args)
|
| 3076 |
+
_cast_core(target, target_format, out_path, f32, ref, _tensor_src_meta(src_path), args, op="tensor_cast", vraw=raw, vqtype=qtype)
|
| 3077 |
|
| 3078 |
|
| 3079 |
def _load_tensor(path, name=None):
|
|
|
|
| 3269 |
"dtype": "F16",
|
| 3270 |
"downloaded_at": _now_iso(),
|
| 3271 |
}
|
| 3272 |
+
# enrich with shape/dtype from the delta file (all tensors, not just the first)
|
| 3273 |
try:
|
| 3274 |
hdr, tens = read_safetensors(str(delta_folder / "voice.safetensors"))
|
| 3275 |
+
keys = [k for k in tens.keys() if k != "delta.voice.marker"]
|
| 3276 |
+
meta["tensors"] = [{"name": k, "shape": list(tens[k]["shape"]), "dtype": tens[k]["dtype"],
|
| 3277 |
+
"bytes": tens[k]["data_offsets"][1] - tens[k]["data_offsets"][0]} for k in keys]
|
| 3278 |
+
if len(keys) == 1:
|
| 3279 |
+
# backward compat: single-tensor voices keep flat fields
|
| 3280 |
+
meta["tensor_name"] = keys[0]
|
| 3281 |
+
meta["shape"] = list(tens[keys[0]]["shape"])
|
| 3282 |
+
meta["dtype"] = tens[keys[0]]["dtype"]
|
| 3283 |
+
meta["bytes"] = tens[keys[0]]["data_offsets"][1]
|
| 3284 |
except Exception:
|
| 3285 |
pass
|
| 3286 |
jtmp = delta_folder / "voice.json.tmp"
|
|
|
|
| 3487 |
b_names = [n for n in b_src.names() if n != "delta.voice.marker"]
|
| 3488 |
if not v_names or not b_names:
|
| 3489 |
_fail(" β No tensors found for delta.")
|
| 3490 |
+
# Match by exact name: every voice tensor needs a same-named base tensor.
|
| 3491 |
+
# Output head first for deterministic order, then voice order.
|
|
|
|
| 3492 |
try:
|
| 3493 |
arch, cfg = _source_arch(v_src)
|
| 3494 |
_cand = _resolve_output_name(v_src, cfg, v_names)
|
|
|
|
|
|
|
| 3495 |
except Exception:
|
| 3496 |
+
_cand = None
|
| 3497 |
+
ordered = ([_cand] if _cand in v_names else []) + [n for n in v_names if n != _cand]
|
| 3498 |
+
missing = [n for n in ordered if n not in b_names]
|
| 3499 |
+
if missing:
|
| 3500 |
+
shown = ", ".join(missing[:5]) + ("β¦" if len(missing) > 5 else "")
|
| 3501 |
+
_fail(f" β {len(missing)} voice tensor(s) have no match in base: {shown}. Delta needs same names on both sides.")
|
| 3502 |
+
pairs = [(n, n) for n in ordered]
|
| 3503 |
# Gates (marker check works for both safetensors header and GGUF? GGUF has no marker, so False unless safetensors delta).
|
| 3504 |
is_voice_delta = _is_delta_safetensors(str(voice_path)) if str(voice_path).endswith(".safetensors") else False
|
| 3505 |
is_base_delta = _is_delta_safetensors(str(base_path)) if str(base_path).endswith(".safetensors") else False
|
|
|
|
| 3518 |
" The voice and base are both delta-voices.",
|
| 3519 |
" delta β delta = style difference of style differences."],
|
| 3520 |
"BIG BRAIN", "delta-on-delta needs explicit confirmation")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3521 |
_assert_not_symlink(delta_folder)
|
| 3522 |
delta_folder.mkdir(parents=True, exist_ok=True)
|
| 3523 |
try:
|
|
|
|
| 3526 |
pass
|
| 3527 |
out_path = delta_folder / "voice.safetensors"
|
| 3528 |
tmp_out = delta_folder / "voice.safetensors.tmp"
|
| 3529 |
+
# Header first (offsets need shapes only), then stream one pair at a
|
| 3530 |
+
# time so packs never hold every tensor in RAM at once.
|
| 3531 |
+
header, off = {}, 0
|
| 3532 |
+
for vname, bname in pairs:
|
| 3533 |
+
vref, bref = v_src.ref(vname), b_src.ref(bname)
|
| 3534 |
+
if tuple(vref.shape) != tuple(bref.shape):
|
| 3535 |
+
_fail(f" β Voice tensor '{vname}' shape {tuple(vref.shape)} doesn't fit base {tuple(bref.shape)}.")
|
| 3536 |
+
nbytes = 2
|
| 3537 |
+
for d in vref.shape:
|
| 3538 |
+
nbytes *= int(d)
|
| 3539 |
+
header[vname] = {"dtype": "F16", "shape": [int(x) for x in vref.shape], "data_offsets": [off, off + nbytes]}
|
| 3540 |
+
off += nbytes
|
| 3541 |
+
header["delta.voice.marker"] = {"dtype": "U8", "shape": [1], "data_offsets": [off, off + 1]}
|
| 3542 |
+
header["__metadata__"] = {"type": "delta", "base": _public_source(str(base_path)), "voice": _public_source(str(voice_path))}
|
| 3543 |
hj = json.dumps(header).encode("utf-8")
|
| 3544 |
with open(tmp_out, "wb") as out:
|
| 3545 |
out.write(struct.pack("<Q", len(hj)))
|
| 3546 |
out.write(hj)
|
| 3547 |
+
for vname, bname in pairs:
|
| 3548 |
+
vref, bref = v_src.ref(vname), b_src.ref(bname)
|
| 3549 |
+
_warn(f" Dequanting {vname} ({vref.dtype}) and base ({bref.dtype}) once to F32 for delta math (unavoidable for GGUF quant).")
|
| 3550 |
+
v_f32, b_f32 = v_src.read_f32(vname), b_src.read_f32(bname)
|
| 3551 |
+
if v_f32.shape != b_f32.shape:
|
| 3552 |
+
sys.stdout.write("\n")
|
| 3553 |
+
_fail(f" β Shape mismatch after dequant for '{vname}': {v_f32.shape} vs {b_f32.shape}.")
|
| 3554 |
+
out.write((v_f32.astype(np.float32) - b_f32.astype(np.float32)).astype("float16").tobytes())
|
| 3555 |
out.write(b"\x01")
|
| 3556 |
out.flush()
|
| 3557 |
try:
|
|
|
|
| 3561 |
tmp_out.replace(out_path)
|
| 3562 |
return out_path
|
| 3563 |
# Original chunked safetensors-safetensors path below (no dequant beyond F32 math per chunk).
|
| 3564 |
+
# Match by exact name: every voice tensor needs a same-named base tensor.
|
| 3565 |
v_keys = [k for k in v_tensors.keys() if k != "delta.voice.marker"]
|
| 3566 |
+
if not v_keys:
|
| 3567 |
+
_fail(" β No tensors found for delta.")
|
| 3568 |
+
missing = [k for k in v_keys if k not in b_tensors]
|
| 3569 |
+
if missing:
|
| 3570 |
+
shown = ", ".join(missing[:5]) + ("β¦" if len(missing) > 5 else "")
|
| 3571 |
+
_fail(f" β {len(missing)} voice tensor(s) have no match in base: {shown}. Delta needs same names on both sides.")
|
| 3572 |
+
pairs = [(k, k) for k in v_keys]
|
| 3573 |
# gates for delta-on-delta niche cases (marker indicates delta file)
|
| 3574 |
is_voice_delta = _is_delta_safetensors(str(voice_path))
|
| 3575 |
is_base_delta = _is_delta_safetensors(str(base_path))
|
|
|
|
| 3587 |
" The voice and base are both delta-voices.",
|
| 3588 |
" delta β delta = style difference of style differences."],
|
| 3589 |
"BIG BRAIN", "delta-on-delta needs explicit confirmation")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3590 |
with open(voice_path, "rb") as f:
|
| 3591 |
v_hs = struct.unpack("<Q", f.read(8))[0]
|
| 3592 |
with open(base_path, "rb") as f:
|
| 3593 |
b_hs = struct.unpack("<Q", f.read(8))[0]
|
| 3594 |
+
specs = []
|
| 3595 |
+
header, off = {}, 0
|
| 3596 |
+
for vname, bname in pairs:
|
| 3597 |
+
vinfo, binfo = v_tensors[vname], b_tensors[bname]
|
| 3598 |
+
vshape, bshape = tuple(vinfo["shape"]), tuple(binfo["shape"])
|
| 3599 |
+
if vshape != bshape:
|
| 3600 |
+
_fail(f" β Voice tensor '{vname}' shape {vshape} doesn't fit base {bshape}.")
|
| 3601 |
+
elems = 1
|
| 3602 |
+
for d in vshape:
|
| 3603 |
+
elems *= int(d)
|
| 3604 |
+
nbytes = elems * 2
|
| 3605 |
+
header[vname] = {"dtype": "F16", "shape": list(vshape), "data_offsets": [off, off + nbytes]}
|
| 3606 |
+
off += nbytes
|
| 3607 |
+
specs.append({"name": vname, "vinfo": vinfo, "binfo": binfo,
|
| 3608 |
+
"v_start": 8 + v_hs + vinfo["data_offsets"][0],
|
| 3609 |
+
"b_start": 8 + b_hs + binfo["data_offsets"][0],
|
| 3610 |
+
"v_bpe": dtype_bytes(vinfo["dtype"]), "b_bpe": dtype_bytes(binfo["dtype"]),
|
| 3611 |
+
"elems": elems})
|
| 3612 |
+
total_elems = off // 2
|
| 3613 |
+
marker_name = "delta.voice.marker"
|
| 3614 |
+
header[marker_name] = {"dtype": "U8", "shape": [1], "data_offsets": [off, off + 1]}
|
| 3615 |
+
header["__metadata__"] = {"type": "delta", "base": _public_source(str(base_path)), "voice": _public_source(str(voice_path))}
|
| 3616 |
+
hj = json.dumps(header).encode("utf-8")
|
| 3617 |
_assert_not_symlink(delta_folder)
|
| 3618 |
delta_folder.mkdir(parents=True, exist_ok=True)
|
| 3619 |
try:
|
|
|
|
| 3622 |
pass
|
| 3623 |
out_path = delta_folder / "voice.safetensors"
|
| 3624 |
tmp_out = delta_folder / "voice.safetensors.tmp"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3625 |
chunk = 1 << 20
|
| 3626 |
total_bytes = total_elems * 2
|
| 3627 |
t0 = time.time()
|
|
|
|
| 3642 |
with open(voice_path, "rb") as fv, open(base_path, "rb") as fb, open(tmp_out, "wb") as out:
|
| 3643 |
out.write(struct.pack("<Q", len(hj)))
|
| 3644 |
out.write(hj)
|
|
|
|
|
|
|
| 3645 |
processed = 0
|
| 3646 |
_delta_progress(force=True)
|
| 3647 |
+
for spec in specs:
|
| 3648 |
+
fv.seek(spec["v_start"])
|
| 3649 |
+
fb.seek(spec["b_start"])
|
| 3650 |
+
vinfo, binfo = spec["vinfo"], spec["binfo"]
|
| 3651 |
+
v_bpe, b_bpe = spec["v_bpe"], spec["b_bpe"]
|
| 3652 |
+
done = 0
|
| 3653 |
+
while done < spec["elems"]:
|
| 3654 |
+
cur = min(chunk, spec["elems"] - done)
|
| 3655 |
+
v_raw = fv.read(cur * v_bpe)
|
| 3656 |
+
b_raw = fb.read(cur * b_bpe)
|
| 3657 |
+
if len(v_raw) != cur * v_bpe or len(b_raw) != cur * b_bpe:
|
| 3658 |
+
sys.stdout.write("\n")
|
| 3659 |
+
_fail(f" β Short read during delta compute ({spec['name']}).")
|
| 3660 |
+
v_f32 = decode_to_f32(v_raw, vinfo["dtype"], (cur,))
|
| 3661 |
+
b_f32 = decode_to_f32(b_raw, binfo["dtype"], (cur,))
|
| 3662 |
+
delta_f16 = (v_f32 - b_f32).astype("float16")
|
| 3663 |
+
out.write(delta_f16.tobytes())
|
| 3664 |
+
done += cur
|
| 3665 |
+
processed += cur
|
| 3666 |
+
_delta_progress()
|
| 3667 |
_delta_progress(force=True)
|
| 3668 |
sys.stdout.write("\n")
|
| 3669 |
# marker byte for auto-detect (delta.voice.marker)
|
|
|
|
| 3908 |
except Exception:
|
| 3909 |
return [" ? Could not read the artifact header; showing voice.json as-is."]
|
| 3910 |
names = [k for k in tens if k != "delta.voice.marker"]
|
| 3911 |
+
claims = meta.get("tensors")
|
| 3912 |
+
if isinstance(claims, list) and claims:
|
| 3913 |
+
for entry in claims:
|
| 3914 |
+
tname = entry.get("name")
|
| 3915 |
+
if tname not in tens:
|
| 3916 |
+
out.append(f" ! MISMATCH tensor: voice.json lists '{tname}', file doesn't hold it.")
|
| 3917 |
+
continue
|
| 3918 |
+
info = tens[tname]
|
| 3919 |
+
if list(entry.get("shape", [])) != list(info["shape"]):
|
| 3920 |
+
out.append(f" ! MISMATCH shape '{tname}': voice.json says {list(entry.get('shape', []))}, "
|
| 3921 |
+
f"file holds {list(info['shape'])} β file wins.")
|
| 3922 |
+
if (entry.get("dtype") or "").upper() != str(info["dtype"]).upper():
|
| 3923 |
+
out.append(f" ! MISMATCH dtype '{tname}': voice.json says {entry.get('dtype')}, "
|
| 3924 |
+
f"file holds {info['dtype']} β file wins.")
|
| 3925 |
+
claimed_names = {e.get("name") for e in claims}
|
| 3926 |
+
for extra in names:
|
| 3927 |
+
if extra not in claimed_names:
|
| 3928 |
+
out.append(f" ! File holds '{extra}' which voice.json doesn't list.")
|
| 3929 |
+
if not names:
|
| 3930 |
+
out.append(" ? No tensors found in the artifact.")
|
| 3931 |
+
has_marker = "delta.voice.marker" in tens
|
| 3932 |
+
if meta.get("type") == "delta" and not has_marker:
|
| 3933 |
+
out.append(" ! voice.json claims delta but the file has no delta marker.")
|
| 3934 |
+
if meta.get("type") != "delta" and has_marker:
|
| 3935 |
+
out.append(" ! File carries a delta marker but voice.json type is "
|
| 3936 |
+
f"'{meta.get('type', 'voice')}'.")
|
| 3937 |
+
return out
|
| 3938 |
tname = meta.get("tensor_name")
|
| 3939 |
if tname not in tens and names:
|
| 3940 |
tname = names[0]
|
|
|
|
| 3987 |
_say(f" Tensor: {meta.get('tensor_name', 'β')}")
|
| 3988 |
_say(f" Shape: {meta.get('shape', 'β')}")
|
| 3989 |
_say(f" Dtype: {meta.get('dtype', 'β')}")
|
| 3990 |
+
if isinstance(meta.get("tensors"), list) and len(meta["tensors"]) != 1:
|
| 3991 |
+
for entry in meta["tensors"]:
|
| 3992 |
+
_say(f" - {entry.get('name')}: {entry.get('shape')} Β· {entry.get('dtype')}")
|
| 3993 |
b = meta.get("bytes")
|
| 3994 |
if isinstance(b, int):
|
| 3995 |
_say(f" Bytes: {b} ({b/1e6:.1f} MB)")
|