File size: 3,250 Bytes
90f10d2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
"""Head-only voice-pack preset (unused by the CLI)."""
import argparse
import json
import urllib.error
from pathlib import Path

from vlib import ctx
from vlib.fetch import _fetch_pack, _resolve_output_name, _source_arch
from vlib.sources import (_family, _hf_config, _public_source,
                          _slugify_voice_name, open_source)
from vlib.tensors import write_safetensors
from vlib.ui import _fail, _note, _now_iso, _say, _step


def _resolve_pack_tensors(source, repo, config, names):
    """Head-only voice-pack preset. Embed/norm/blocks stay manual
    (voice get <repo> <tensors...>).
    Config is the source config with _hf_config fallback. Miss fails loud."""
    if config is None and getattr(source, "repo", None):
        config = _hf_config(source.repo)
    head = _resolve_output_name(source, config, names)
    if head is None:
        _fail(f"  ✗ pack: no output head found in '{repo}'.")
    return [head]


def cmd_pack(args):
    repo = args.repo.strip().rstrip("/")
    if not repo:
        _fail("  ✗ Enter a model name — for example: voice pack google/gemma-3-1b")
    _step(f"Finding the voice pack in {repo}…")
    try:
        source = open_source(repo)
        names = source.names()
        if not names:
            _fail(f"  ✗ No tensors found in '{repo}'.")
        arch, config = _source_arch(source)
    except urllib.error.HTTPError as e:
        _fail(f"  ✗ Model '{repo}' was not found or is not accessible ({e.code}).")
    except Exception as e:
        if ctx.VERBOSE:
            import traceback
            _say(traceback.format_exc())
        _fail(f"  ✗ Could not reach the model. Check your connection ({type(e).__name__}).")

    _step("Resolving voice-pack tensors…")
    resolved = _resolve_pack_tensors(source, repo, config, names)
    for tname in resolved:
        _note(f"  {tname}")
    # Fetch via the same multi path as get --multi, then save flat in the
    # registry through the existing add machinery (no new storage format).
    pack = _fetch_pack(source, repo, resolved)
    slug = _slugify_voice_name(repo.split("/")[-1].split("@")[0].split(":")[0].replace(".gguf", "").replace(".safetensors", ""))
    name = f"pack-{slug}"
    import tempfile, shutil
    stage = Path(tempfile.mkdtemp(prefix="voicepack-stage-"))
    try:
        st_path = stage / "voice.safetensors"
        write_safetensors(pack, str(st_path))
        js_path = stage / "voice.json"
        js_path.write_text(json.dumps({
            "name": name,
            "source": _public_source(repo),
            "source_format": source.kind,
            "tensors": [{"name": n, "shape": [int(x) for x in pack[n][2]], "dtype": pack[n][1]}
                        for n in resolved if n in pack],
            "architecture": arch,
            "family": _family(arch),
            "downloaded_at": _now_iso(),
        }, indent=2) + "\n")
        from vlib.cmds_voices import cmd_add
        cmd_add(argparse.Namespace(safetensors=str(st_path), json=str(js_path), name=name,
                                   yes=getattr(args, "yes", False),
                                   non_interactive=getattr(args, "non_interactive", False)))
    finally:
        shutil.rmtree(stage, ignore_errors=True)