Upload slimshard_gui.py
Browse files- slimshard_gui.py +165 -14
slimshard_gui.py
CHANGED
|
@@ -46,10 +46,14 @@ if __package__ in (None, ""):
|
|
| 46 |
from .cuda_backend import cuda_device_name, cuda_is_available
|
| 47 |
from .slimshard import (
|
| 48 |
HPCConfig,
|
|
|
|
| 49 |
build_slimshard,
|
| 50 |
export_hf_bundle,
|
| 51 |
export_safetensors_to_gguf,
|
|
|
|
| 52 |
inspect_recovery_plan,
|
|
|
|
|
|
|
| 53 |
recover_from_shard,
|
| 54 |
regenerate,
|
| 55 |
run_deepseek_v41_generate,
|
|
@@ -98,6 +102,12 @@ class SlimShardGUI:
|
|
| 98 |
|
| 99 |
def _init_vars(self) -> None:
|
| 100 |
self.op_mode_var = tk.StringVar(value="recover")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
self.shard_var = tk.StringVar()
|
| 102 |
self.index_var = tk.StringVar()
|
| 103 |
self.config_var = tk.StringVar()
|
|
@@ -138,6 +148,10 @@ class SlimShardGUI:
|
|
| 138 |
|
| 139 |
for v in (
|
| 140 |
self.op_mode_var,
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
self.shard_var,
|
| 142 |
self.index_var,
|
| 143 |
self.config_var,
|
|
@@ -253,10 +267,16 @@ class SlimShardGUI:
|
|
| 253 |
)
|
| 254 |
ttk.Radiobutton(
|
| 255 |
mode_bar,
|
| 256 |
-
text="CheckFree Single-Shard Recovery (
|
| 257 |
variable=self.op_mode_var,
|
| 258 |
value="recover",
|
| 259 |
).pack(side=tk.LEFT, padx=(0, 14))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 260 |
ttk.Radiobutton(
|
| 261 |
mode_bar,
|
| 262 |
text="Build Self-Contained SlimShard (Multi-Shard -> 1 SlimShard)",
|
|
@@ -276,44 +296,71 @@ class SlimShardGUI:
|
|
| 276 |
value="export-gguf",
|
| 277 |
).pack(side=tk.LEFT)
|
| 278 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
self._add_path_row(
|
| 280 |
io_frame,
|
| 281 |
-
|
| 282 |
-
"Surviving Shard / Input (.safetensors):",
|
| 283 |
self.shard_var,
|
| 284 |
self._pick_shard,
|
| 285 |
)
|
| 286 |
self._add_path_row(
|
| 287 |
io_frame,
|
| 288 |
-
|
| 289 |
"Index JSON (optional, auto-inferred if omitted):",
|
| 290 |
self.index_var,
|
| 291 |
self._pick_index,
|
| 292 |
)
|
| 293 |
self._add_path_row(
|
| 294 |
io_frame,
|
| 295 |
-
|
| 296 |
"Config JSON (auto-detected from folder):",
|
| 297 |
self.config_var,
|
| 298 |
self._pick_config,
|
| 299 |
)
|
| 300 |
self._add_path_row(
|
| 301 |
io_frame,
|
| 302 |
-
|
| 303 |
-
"Source
|
| 304 |
self.model_dir_var,
|
| 305 |
self._pick_model_dir,
|
| 306 |
)
|
| 307 |
self._add_path_row(
|
| 308 |
io_frame,
|
| 309 |
-
|
| 310 |
-
"Output .safetensors File:",
|
| 311 |
self.output_var,
|
| 312 |
self._pick_output,
|
| 313 |
)
|
| 314 |
self._add_path_row(
|
| 315 |
io_frame,
|
| 316 |
-
|
| 317 |
"Export Ready-to-Run HF Bundle Dir:",
|
| 318 |
self.export_dir_var,
|
| 319 |
self._pick_export_dir,
|
|
@@ -375,6 +422,27 @@ class SlimShardGUI:
|
|
| 375 |
width=5,
|
| 376 |
).pack(side=tk.LEFT, padx=(0, 14))
|
| 377 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 378 |
ttk.Label(row_opt, text="SlimMoE Keep Ratio:").pack(
|
| 379 |
side=tk.LEFT, padx=(0, 6)
|
| 380 |
)
|
|
@@ -638,14 +706,97 @@ class SlimShardGUI:
|
|
| 638 |
self.test_model_dir_var.set(self.export_dir_var.get())
|
| 639 |
|
| 640 |
def _pick_shard(self) -> None:
|
| 641 |
-
|
| 642 |
-
title="Select Surviving .safetensors Shard",
|
| 643 |
filetypes=[("SafeTensors", "*.safetensors"), ("All files", "*.*")],
|
| 644 |
)
|
| 645 |
-
if
|
| 646 |
-
|
|
|
|
|
|
|
| 647 |
self._inspect_plan()
|
| 648 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 649 |
def _pick_index(self) -> None:
|
| 650 |
p = filedialog.askopenfilename(
|
| 651 |
title="Select model.safetensors.index.json",
|
|
|
|
| 46 |
from .cuda_backend import cuda_device_name, cuda_is_available
|
| 47 |
from .slimshard import (
|
| 48 |
HPCConfig,
|
| 49 |
+
_resolve_shard_paths,
|
| 50 |
build_slimshard,
|
| 51 |
export_hf_bundle,
|
| 52 |
export_safetensors_to_gguf,
|
| 53 |
+
fetch_minimum_shards_from_hf,
|
| 54 |
inspect_recovery_plan,
|
| 55 |
+
parse_hf_repo_url,
|
| 56 |
+
plan_minimum_shards_from_hf,
|
| 57 |
recover_from_shard,
|
| 58 |
regenerate,
|
| 59 |
run_deepseek_v41_generate,
|
|
|
|
| 102 |
|
| 103 |
def _init_vars(self) -> None:
|
| 104 |
self.op_mode_var = tk.StringVar(value="recover")
|
| 105 |
+
self.hf_url_var = tk.StringVar(
|
| 106 |
+
value="https://huggingface.co/deepseek-ai/DeepSeek-V4.1-Flash-1017/tree/main"
|
| 107 |
+
)
|
| 108 |
+
self.hf_fetch_mode_var = tk.StringVar(value="surgical")
|
| 109 |
+
self.num_anchor_layers_var = tk.IntVar(value=3)
|
| 110 |
+
self.anchor_layers_var = tk.StringVar(value="")
|
| 111 |
self.shard_var = tk.StringVar()
|
| 112 |
self.index_var = tk.StringVar()
|
| 113 |
self.config_var = tk.StringVar()
|
|
|
|
| 148 |
|
| 149 |
for v in (
|
| 150 |
self.op_mode_var,
|
| 151 |
+
self.hf_url_var,
|
| 152 |
+
self.hf_fetch_mode_var,
|
| 153 |
+
self.num_anchor_layers_var,
|
| 154 |
+
self.anchor_layers_var,
|
| 155 |
self.shard_var,
|
| 156 |
self.index_var,
|
| 157 |
self.config_var,
|
|
|
|
| 267 |
)
|
| 268 |
ttk.Radiobutton(
|
| 269 |
mode_bar,
|
| 270 |
+
text="CheckFree Multi/Single-Shard Recovery (Shards -> Full Model)",
|
| 271 |
variable=self.op_mode_var,
|
| 272 |
value="recover",
|
| 273 |
).pack(side=tk.LEFT, padx=(0, 14))
|
| 274 |
+
ttk.Radiobutton(
|
| 275 |
+
mode_bar,
|
| 276 |
+
text="Retrieve Minimum Shards from HF Link",
|
| 277 |
+
variable=self.op_mode_var,
|
| 278 |
+
value="fetch-hf",
|
| 279 |
+
).pack(side=tk.LEFT, padx=(0, 14))
|
| 280 |
ttk.Radiobutton(
|
| 281 |
mode_bar,
|
| 282 |
text="Build Self-Contained SlimShard (Multi-Shard -> 1 SlimShard)",
|
|
|
|
| 296 |
value="export-gguf",
|
| 297 |
).pack(side=tk.LEFT)
|
| 298 |
|
| 299 |
+
# Hugging Face Link Minimum-Shard Retriever Bar
|
| 300 |
+
hf_bar = ttk.Frame(io_frame)
|
| 301 |
+
hf_bar.grid(row=1, column=0, columnspan=3, sticky=tk.EW, pady=(0, 6))
|
| 302 |
+
hf_bar.columnconfigure(1, weight=1)
|
| 303 |
+
ttk.Label(
|
| 304 |
+
hf_bar, text="Hugging Face Link / Repo:", font=("Sans", 9, "bold")
|
| 305 |
+
).grid(row=0, column=0, sticky=tk.W, padx=(0, 6))
|
| 306 |
+
ttk.Entry(hf_bar, textvariable=self.hf_url_var).grid(
|
| 307 |
+
row=0, column=1, sticky=tk.EW, padx=(0, 8)
|
| 308 |
+
)
|
| 309 |
+
ttk.Combobox(
|
| 310 |
+
hf_bar,
|
| 311 |
+
textvariable=self.hf_fetch_mode_var,
|
| 312 |
+
values=("surgical", "full_shards"),
|
| 313 |
+
width=12,
|
| 314 |
+
state="readonly",
|
| 315 |
+
).grid(row=0, column=2, padx=(0, 6))
|
| 316 |
+
ttk.Button(
|
| 317 |
+
hf_bar, text="Analyze Min Shards", command=self._analyze_hf_min_shards
|
| 318 |
+
).grid(row=0, column=3, padx=(0, 6))
|
| 319 |
+
ttk.Button(
|
| 320 |
+
hf_bar,
|
| 321 |
+
text="Retrieve Min Shards",
|
| 322 |
+
style="Accent.TButton",
|
| 323 |
+
command=self._retrieve_hf_min_shards,
|
| 324 |
+
).grid(row=0, column=4)
|
| 325 |
+
|
| 326 |
self._add_path_row(
|
| 327 |
io_frame,
|
| 328 |
+
2,
|
| 329 |
+
"Surviving Shard(s) / Input (.safetensors, comma-sep):",
|
| 330 |
self.shard_var,
|
| 331 |
self._pick_shard,
|
| 332 |
)
|
| 333 |
self._add_path_row(
|
| 334 |
io_frame,
|
| 335 |
+
3,
|
| 336 |
"Index JSON (optional, auto-inferred if omitted):",
|
| 337 |
self.index_var,
|
| 338 |
self._pick_index,
|
| 339 |
)
|
| 340 |
self._add_path_row(
|
| 341 |
io_frame,
|
| 342 |
+
4,
|
| 343 |
"Config JSON (auto-detected from folder):",
|
| 344 |
self.config_var,
|
| 345 |
self._pick_config,
|
| 346 |
)
|
| 347 |
self._add_path_row(
|
| 348 |
io_frame,
|
| 349 |
+
5,
|
| 350 |
+
"Source / Download Model Dir:",
|
| 351 |
self.model_dir_var,
|
| 352 |
self._pick_model_dir,
|
| 353 |
)
|
| 354 |
self._add_path_row(
|
| 355 |
io_frame,
|
| 356 |
+
6,
|
| 357 |
+
"Output .safetensors / .gguf File:",
|
| 358 |
self.output_var,
|
| 359 |
self._pick_output,
|
| 360 |
)
|
| 361 |
self._add_path_row(
|
| 362 |
io_frame,
|
| 363 |
+
7,
|
| 364 |
"Export Ready-to-Run HF Bundle Dir:",
|
| 365 |
self.export_dir_var,
|
| 366 |
self._pick_export_dir,
|
|
|
|
| 422 |
width=5,
|
| 423 |
).pack(side=tk.LEFT, padx=(0, 14))
|
| 424 |
|
| 425 |
+
ttk.Label(row_opt, text="# Anchor Layers:").pack(
|
| 426 |
+
side=tk.LEFT, padx=(0, 4)
|
| 427 |
+
)
|
| 428 |
+
ttk.Spinbox(
|
| 429 |
+
row_opt,
|
| 430 |
+
from_=1,
|
| 431 |
+
to=128,
|
| 432 |
+
increment=1,
|
| 433 |
+
textvariable=self.num_anchor_layers_var,
|
| 434 |
+
width=4,
|
| 435 |
+
).pack(side=tk.LEFT, padx=(0, 10))
|
| 436 |
+
|
| 437 |
+
ttk.Label(row_opt, text="Anchor Layers (e.g. 0,14,39):").pack(
|
| 438 |
+
side=tk.LEFT, padx=(0, 4)
|
| 439 |
+
)
|
| 440 |
+
ttk.Entry(
|
| 441 |
+
row_opt,
|
| 442 |
+
textvariable=self.anchor_layers_var,
|
| 443 |
+
width=12,
|
| 444 |
+
).pack(side=tk.LEFT, padx=(0, 12))
|
| 445 |
+
|
| 446 |
ttk.Label(row_opt, text="SlimMoE Keep Ratio:").pack(
|
| 447 |
side=tk.LEFT, padx=(0, 6)
|
| 448 |
)
|
|
|
|
| 706 |
self.test_model_dir_var.set(self.export_dir_var.get())
|
| 707 |
|
| 708 |
def _pick_shard(self) -> None:
|
| 709 |
+
ps = filedialog.askopenfilenames(
|
| 710 |
+
title="Select Surviving .safetensors Shard(s) (Multi-Select Supported)",
|
| 711 |
filetypes=[("SafeTensors", "*.safetensors"), ("All files", "*.*")],
|
| 712 |
)
|
| 713 |
+
if ps:
|
| 714 |
+
paths = [Path(p) for p in ps]
|
| 715 |
+
self._set_shard_and_companions(paths[0])
|
| 716 |
+
self.shard_var.set(", ".join(str(p) for p in paths))
|
| 717 |
self._inspect_plan()
|
| 718 |
|
| 719 |
+
def _analyze_hf_min_shards(self) -> None:
|
| 720 |
+
url = self.hf_url_var.get().strip()
|
| 721 |
+
if not url:
|
| 722 |
+
messagebox.showwarning("SlimShard", "Please enter a Hugging Face link or repo ID first.")
|
| 723 |
+
return
|
| 724 |
+
try:
|
| 725 |
+
tp = float(self.target_params_b_var.get())
|
| 726 |
+
except Exception:
|
| 727 |
+
tp = 0.0
|
| 728 |
+
try:
|
| 729 |
+
me = int(self.max_experts_var.get())
|
| 730 |
+
except Exception:
|
| 731 |
+
me = 0
|
| 732 |
+
try:
|
| 733 |
+
n_anc = int(self.num_anchor_layers_var.get())
|
| 734 |
+
except Exception:
|
| 735 |
+
n_anc = 3
|
| 736 |
+
anc_str = self.anchor_layers_var.get().strip() or None
|
| 737 |
+
|
| 738 |
+
self._log(f"Analyzing minimum shards on Hugging Face for {url}...")
|
| 739 |
+
try:
|
| 740 |
+
plan = plan_minimum_shards_from_hf(
|
| 741 |
+
url,
|
| 742 |
+
target_params_b=tp if tp > 0.0 else None,
|
| 743 |
+
max_experts=me if me > 0 else None,
|
| 744 |
+
num_anchor_layers=n_anc,
|
| 745 |
+
anchor_layers=anc_str,
|
| 746 |
+
)
|
| 747 |
+
except Exception as exc:
|
| 748 |
+
self._log(f"HF Analyze Failed: {exc}")
|
| 749 |
+
messagebox.showerror("HF Minimum Shards Analysis Failed", str(exc))
|
| 750 |
+
return
|
| 751 |
+
|
| 752 |
+
repo_name = plan["repo_id"].split("/")[-1]
|
| 753 |
+
if not self.model_dir_var.get().strip():
|
| 754 |
+
self.model_dir_var.set(str(Path.cwd() / repo_name))
|
| 755 |
+
if not self.output_var.get().strip():
|
| 756 |
+
self.output_var.set(str(Path.cwd() / repo_name / "model.safetensors"))
|
| 757 |
+
if not self.export_dir_var.get().strip():
|
| 758 |
+
self.export_dir_var.set(str(Path.cwd() / f"recovered_{repo_name.lower().replace('-', '_')}"))
|
| 759 |
+
|
| 760 |
+
min_sh = plan["minimum_shards"]
|
| 761 |
+
tot_sh = plan["total_repo_shards"]
|
| 762 |
+
anchors = plan["anchor_layers"]
|
| 763 |
+
archs = ",".join(plan["architectures"]) or plan["model_type"]
|
| 764 |
+
self.arch_summary_var.set(
|
| 765 |
+
f"{plan['repo_id']} ({archs} | ~{plan['estimated_params_b']}B target | "
|
| 766 |
+
f"layers={plan['num_hidden_layers']}, experts={plan['kept_experts']}/{plan['orig_num_experts']})"
|
| 767 |
+
)
|
| 768 |
+
self.layers_summary_var.set(
|
| 769 |
+
f"Minimum Shards Needed: {len(min_sh)} / {tot_sh} ({', '.join(min_sh)}) | "
|
| 770 |
+
f"Anchor Layers ({len(anchors)}): {anchors}"
|
| 771 |
+
)
|
| 772 |
+
self.tele_breakdown_var.set(
|
| 773 |
+
f"min_shards={len(min_sh)}/{tot_sh} | surgical_tensors={plan['surgical_tensor_count']} | "
|
| 774 |
+
f"full_repo={plan['total_repo_size_gb']} GB"
|
| 775 |
+
)
|
| 776 |
+
roles_lines = [
|
| 777 |
+
f" - {sh}: {', '.join(plan['shard_roles'].get(sh, []))}" for sh in min_sh
|
| 778 |
+
]
|
| 779 |
+
msg = (
|
| 780 |
+
f"HF Repo: {plan['repo_id']}@{plan['revision']}\n"
|
| 781 |
+
f"Minimum Shards Required: {len(min_sh)} of {tot_sh} shards\n"
|
| 782 |
+
f"Selected Anchor Layers: {anchors}\n"
|
| 783 |
+
f"Surgical Tensor Count: {plan['surgical_tensor_count']} tensors\n"
|
| 784 |
+
+ "\n".join(roles_lines)
|
| 785 |
+
)
|
| 786 |
+
self._log(msg)
|
| 787 |
+
|
| 788 |
+
def _retrieve_hf_min_shards(self) -> None:
|
| 789 |
+
self.op_mode_var.set("fetch-hf")
|
| 790 |
+
if not self.model_dir_var.get().strip():
|
| 791 |
+
try:
|
| 792 |
+
repo_id, _ = parse_hf_repo_url(self.hf_url_var.get().strip())
|
| 793 |
+
self.model_dir_var.set(str(Path.cwd() / repo_id.split("/")[-1]))
|
| 794 |
+
except Exception:
|
| 795 |
+
self.model_dir_var.set(str(Path.cwd() / "hf_min_shards"))
|
| 796 |
+
if not self.output_var.get().strip():
|
| 797 |
+
self.output_var.set(str(Path(self.model_dir_var.get().strip()) / "model.safetensors"))
|
| 798 |
+
self._start_run()
|
| 799 |
+
|
| 800 |
def _pick_index(self) -> None:
|
| 801 |
p = filedialog.askopenfilename(
|
| 802 |
title="Select model.safetensors.index.json",
|