mirror of
https://github.com/Niko1221/Strata.git
synced 2026-11-07 14:21:06 -10:00
Keep Unsloth's 1-bit DeepSeek sets off the Mac until the Metal engine has an IQ1_S kernel (lists, --model, --model-path, app check, setup)
This commit is contained in:
1 parent
ad4755c249
commit
32806f82aa
8 files changed
+226
No files matched your search
@@ -8401,9 +8401,13 @@ def install_dsv41(a, family, gpu, hip, amd, cuda_tk, multi, port, ram, adopted,
|
||||
except Exception: # noqa: BLE001 - the disk is only a hint here
|
||||
room = None
|
||||
rec = dsv41_fit.recommend(ram, room) # the best version this computer's memory and disk suit; warn-never-refuse
|
||||
if mac: # the Unsloth 1-bit sets have no Metal kernel yet: not in a Mac's menu
|
||||
names = [m for m in names if m not in dsv41_fit.MAC_NOT_YET]
|
||||
for i, m in enumerate(names, 1):
|
||||
say(f" {i}) {fam['models'][m].get('label', m)}: {fam['models'][m]['about']}" + (" (recommended)" if m == rec else ""))
|
||||
model = names[int(ask("Which version?", [str(i) for i in range(1, len(names) + 1)], str(names.index(rec) + 1), a.yes)) - 1]
|
||||
if mac and model in dsv41_fit.MAC_NOT_YET: # IQ1_S experts: no Metal kernel yet (strata_control/dsv41_fit.MAC_NOT_YET); said before anything is fetched or packed
|
||||
fail(dsv41_fit.MAC_NOT_YET_LINE, "choose --model IST-DAS-2.00bpw or --model IST-DAS-2.25bpw (their files work with --gguf-dir too); nothing has been downloaded")
|
||||
ok(f"size: {model}")
|
||||
md = fam["models"][model]
|
||||
say(" Its GGUF files: " + ("IST-DAS / ISTA-DASLab (huggingface.co/ISTA-DASLab/DeepSeek-V4.1-Flash-1017-GSQ-RCO-GGUF), with the DSpark drafter and a small metadata file from Unsloth"
|
||||
|
||||
@@ -383,6 +383,8 @@ class Catalog:
|
||||
near = S.closest(family, list(families))
|
||||
raise ValueError(f"There is no model family called '{family}'." + (f" Did you mean '{near}'?" if near else "")
|
||||
+ " Families: " + ", ".join(families) + ".")
|
||||
if match in self.strata.dsv41_not_on_mac() and self._mac_hardware():
|
||||
raise ValueError(self.strata.dsv41_not_on_mac_line() + " Use ist-das-2.00bpw or ist-das-2.25bpw.")
|
||||
offered = [f for f in families if match in self.strata.sizes_of(models, f)]
|
||||
if family is not None:
|
||||
if family not in offered:
|
||||
@@ -497,6 +499,12 @@ class Catalog:
|
||||
vram = comp.main_vram_gb
|
||||
return VR.default_mode(vram, "intel" if comp.engine_vendor == "intel" else "") if vram > 0 else "cpu" # no usable card: nothing to read on, the CPU reader
|
||||
|
||||
def _mac_hardware(self) -> bool:
|
||||
try:
|
||||
return bool(self.hardware().get("mac"))
|
||||
except Exception: # noqa: BLE001 - no hardware report: not a Mac as far as this check knows
|
||||
return False
|
||||
|
||||
def is_mac(self) -> bool:
|
||||
"""An Apple Silicon Mac: the chip's memory is the system's (the Metal engine; strata_control/vision_reader.py)."""
|
||||
return self.engine_backend() == "metal"
|
||||
|
||||
@@ -1309,6 +1309,10 @@ class Runner:
|
||||
if found is None:
|
||||
return False
|
||||
model, d, folder = found
|
||||
if sys.platform == "darwin" and model in self.catalog.strata.dsv41_not_on_mac(): # IQ1_S experts: no Metal kernel yet; said before an engine or a download is touched
|
||||
raise LaunchError(self.catalog.strata.dsv41_not_on_mac_line(),
|
||||
"Use the 2.00 or 2.25 files (--model ist-das-2.00bpw or --model ist-das-2.25bpw, or --model-path to those files). "
|
||||
"Nothing was downloaded or changed.", 2)
|
||||
if s.family and s.family != "deepseek41":
|
||||
raise LaunchError(f"These are DeepSeek-V4.1-Flash's files, not {s.family} {s.model}'s.",
|
||||
"Leave --model out, or use --model " + model + ".", 2)
|
||||
|
||||
@@ -9,6 +9,7 @@ from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import threading
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
@@ -559,6 +560,9 @@ class ModelScreen(Base):
|
||||
if found is None:
|
||||
return False
|
||||
model, d, folder = found
|
||||
if sys.platform == "darwin" and model in self.ctx.catalog.strata.dsv41_not_on_mac():
|
||||
self.say(self.ctx.catalog.strata.dsv41_not_on_mac_line() + " Pick the 2.00 or 2.25 version from the list, or point to those files.", True)
|
||||
return True
|
||||
_layout, names, missing = mod.dsv41_parts(d, lambda n: (folder / n).exists()) # the pinned four-shard layout, or ISTA's six-shard one (0.2.8.1)
|
||||
if missing:
|
||||
self.say(f"These are DeepSeek-V4.1-Flash {d.get('label', model)} files, but {len(missing)} of {len(names)} parts are not in {folder} "
|
||||
|
||||
@@ -607,6 +607,11 @@ def mac_plan(ram_gb: float, quant: str = "IQ2_XS", disk_free_gb: float | None =
|
||||
# the 1.75 bpw set shares the 2.00 set's IQ1_M kernel but was never run there, so it is not offered on a Mac.
|
||||
|
||||
MAC_SET_PACK = {"IST-DAS-2.00bpw": "IQ2_XXS", "IST-DAS-2.25bpw": "IQ2_XS"}
|
||||
# Unsloth's UD-IQ1_S / UD-IQ1_M (0.2.9) carry IQ1_S experts, which the Metal DeepSeek engine has no kernel for yet (metal/src/engine/msl/ds4_iq.metal:
|
||||
# IQ1_S and IQ4_NL are CUDA / HIP only). They are kept off the Mac everywhere: the model lists (MAC_SET_PACK above does that on its own), setup, the
|
||||
# launch and the "I already have the model" check, with this one line. Remove a name here when its Metal kernel lands.
|
||||
MAC_NOT_YET = ("UD-IQ1_S", "UD-IQ1_M")
|
||||
MAC_NOT_YET_LINE = "Unsloth's 1-bit DeepSeek sets don't run on a Mac yet; the 2.00 and 2.25 sets do."
|
||||
MAC_SMALL_RAM_GB = 64.0 # below this the measured M5 Max numbers do not apply (nothing smaller than 32 GB was ever started)
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import json
|
||||
import os
|
||||
import re
|
||||
import stat
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from strata_control import discovery
|
||||
@@ -336,6 +337,7 @@ def _already_set_up(shards: list[Path], candidate: dict | None) -> dict | None:
|
||||
|
||||
|
||||
ISSUE_WORDS = {
|
||||
"not_on_this_mac": "Unsloth's 1-bit DeepSeek sets don't run on a Mac yet; the 2.00 and 2.25 sets do",
|
||||
"multiple_model_groups": "the folder holds more than one model",
|
||||
"incomplete_or_invalid_shard_set": "some parts of the model are missing",
|
||||
"unrecognized_or_unsupported_model_variant": "this is not a model size Strata knows",
|
||||
@@ -366,6 +368,20 @@ _REASON_WORDS = (
|
||||
)
|
||||
|
||||
|
||||
def _on_mac() -> bool:
|
||||
return sys.platform == "darwin"
|
||||
|
||||
|
||||
def _mac_not_yet() -> tuple:
|
||||
from strata_control import dsv41_fit
|
||||
return dsv41_fit.MAC_NOT_YET
|
||||
|
||||
|
||||
def _mac_not_yet_line() -> str:
|
||||
from strata_control import dsv41_fit
|
||||
return dsv41_fit.MAC_NOT_YET_LINE
|
||||
|
||||
|
||||
def plain_issue(code: str) -> str:
|
||||
"""One problem from the GGUF preview, in words, with the file's name when it is about one file
|
||||
(`file.gguf: GGUF shard is not a nonempty regular file`)."""
|
||||
@@ -667,6 +683,10 @@ def _dsv41_plan(path: Path, details) -> dict | None:
|
||||
candidate = {"family": dsv41_sets.FAMILY, "model": row["model"], "basis": "known_names_and_sizes", "expected_expert_types": [],
|
||||
"expected_tensor_count": None,
|
||||
"setup_args_hint": ["--family", dsv41_sets.FAMILY, "--model", row["model"], "--gguf-dir", str(place)]}
|
||||
if _on_mac() and row["model"] in _mac_not_yet(): # recommend, never force: say what does work and stop before anything is copied or packed
|
||||
issues.append("not_on_this_mac")
|
||||
candidate = None
|
||||
messages.append(_mac_not_yet_line())
|
||||
messages.append(f"This is DeepSeek-V4.1-Flash, {row['label']}: all {len(row['present'])} model files are here"
|
||||
+ (", and the n-gram table." if row["engram"] else "."))
|
||||
if not row["engram"]:
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
"""Unsloth's UD-IQ1_S / UD-IQ1_M DeepSeek sets on a Mac (0.2.9): their IQ1_S experts have no Metal kernel yet, so the sets are not offered there
|
||||
(model list, `--model`, `--model-path`, the app's "I already have the model" check, setup), and the line says what does work.
|
||||
On every other computer nothing changes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
from strata_cli.launch import settings as S
|
||||
from strata_cli.launch.catalog import Catalog
|
||||
from strata_cli.launch.runner import LaunchError, Runner
|
||||
from strata_control import dsv41_fit, dsv41_sets, models
|
||||
from tests.terminal.launch_support import HW_12, Home, catalog
|
||||
|
||||
LINE = "Unsloth's 1-bit DeepSeek sets don't run on a Mac yet; the 2.00 and 2.25 sets do."
|
||||
MAC = {**HW_12, "mac": True, "ram_gb": 128.0, "disk_free_gb": 900.0, "gpus": [], "engine_vendor": "apple"}
|
||||
PC = {**HW_12, "ram_gb": 64.0, "disk_free_gb": 900.0, "engine_vendor": "nvidia"}
|
||||
UD = ("UD-IQ1_S", "UD-IQ1_M")
|
||||
|
||||
|
||||
def ud_files(model):
|
||||
return {**dsv41_sets.tables()["sets"][model]["shards"], **dsv41_sets.tables()["sets"][model]["engram_alt"]}
|
||||
|
||||
|
||||
class Line(unittest.TestCase):
|
||||
def test_the_line_and_the_names(self):
|
||||
self.assertEqual(dsv41_fit.MAC_NOT_YET, UD)
|
||||
self.assertEqual(dsv41_fit.MAC_NOT_YET_LINE, LINE)
|
||||
self.assertFalse(set(UD) & set(dsv41_fit.MAC_SET_PACK))
|
||||
|
||||
|
||||
class Lists(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.home = Home().__enter__()
|
||||
self.addCleanup(self.home.__exit__, None, None, None)
|
||||
self.hw = dict(MAC)
|
||||
p = mock.patch.object(Catalog, "hardware", lambda s, fresh=False: dict(self.hw))
|
||||
p.start()
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def models(self):
|
||||
return [r.model for r in catalog(self.hw).rows() if r.family == "deepseek41"]
|
||||
|
||||
def test_a_mac_is_not_offered_the_unsloth_sets(self):
|
||||
got = self.models()
|
||||
for m in UD:
|
||||
self.assertNotIn(m, got)
|
||||
self.assertFalse(set(got) - set(dsv41_fit.MAC_SET_PACK), got)
|
||||
|
||||
def test_a_pc_is_offered_them(self):
|
||||
self.hw = dict(PC)
|
||||
got = self.models()
|
||||
for m in UD:
|
||||
self.assertIn(m, got)
|
||||
|
||||
def test_model_flag_on_a_mac_says_what_works(self):
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
catalog(self.hw).resolve("ud-iq1_m", None)
|
||||
self.assertIn(LINE, str(cm.exception))
|
||||
self.assertIn("ist-das-2.25bpw", str(cm.exception))
|
||||
self.assertEqual(catalog(self.hw).resolve("ist-das-2.25bpw"), ("deepseek41", "IST-DAS-2.25bpw"))
|
||||
|
||||
def test_model_flag_on_a_pc_is_unchanged(self):
|
||||
self.hw = dict(PC)
|
||||
self.assertEqual(catalog(self.hw).resolve("ud-iq1_s"), ("deepseek41", "UD-IQ1_S"))
|
||||
|
||||
|
||||
class OwnFiles(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.home = Home().__enter__()
|
||||
self.addCleanup(self.home.__exit__, None, None, None)
|
||||
self.hw = dict(MAC)
|
||||
p = mock.patch.object(Catalog, "hardware", lambda s, fresh=False: dict(self.hw))
|
||||
p.start()
|
||||
self.addCleanup(p.stop)
|
||||
|
||||
def folder(self, model):
|
||||
folder = Path(os.environ["STRATA_INSTALL_DIR"]) / model
|
||||
folder.mkdir(parents=True, exist_ok=True)
|
||||
for name in dsv41_sets.tables()["sets"][model]["shards"]:
|
||||
(folder / name).write_bytes(b"GGUF")
|
||||
return folder
|
||||
|
||||
def test_model_path_of_a_ud_folder_on_a_mac_stops_with_the_line(self):
|
||||
for model in UD:
|
||||
folder = self.folder(model)
|
||||
with mock.patch.object(sys, "platform", "darwin"):
|
||||
with self.assertRaises(LaunchError) as cm:
|
||||
Runner(S.LaunchSettings(model_path=str(folder)), catalog=catalog(self.hw)).check_own_files()
|
||||
self.assertIn(LINE, cm.exception.text)
|
||||
self.assertIn("2.25", cm.exception.hint if hasattr(cm.exception, "hint") else cm.exception.text)
|
||||
|
||||
def test_the_same_folder_on_another_computer_is_the_model(self):
|
||||
folder = self.folder("UD-IQ1_S")
|
||||
with mock.patch.object(sys, "platform", "linux"):
|
||||
r = Runner(S.LaunchSettings(model_path=str(folder)), catalog=catalog(self.hw))
|
||||
r.check_own_files()
|
||||
self.assertEqual((r.s.family, r.s.model), ("deepseek41", "UD-IQ1_S"))
|
||||
|
||||
|
||||
class AppCheck(unittest.TestCase):
|
||||
def preview(self, model, platform):
|
||||
with mock.patch.object(models, "_dsv41_files", return_value=ud_files(model)), mock.patch.object(sys, "platform", platform), \
|
||||
__import__("tempfile").TemporaryDirectory() as tmp:
|
||||
return models.plan_import(tmp)
|
||||
|
||||
def test_on_a_mac_the_preview_says_so_and_selects_nothing(self):
|
||||
for model in UD:
|
||||
plan = self.preview(model, "darwin")
|
||||
self.assertIn(LINE, plan["messages"])
|
||||
self.assertIn("not_on_this_mac", plan["issues"])
|
||||
self.assertEqual(plan["file_set_status"], "invalid_or_incomplete")
|
||||
self.assertIsNone(plan["candidate_selection"])
|
||||
self.assertIn(LINE.rstrip("."), plan["plain_issues"])
|
||||
|
||||
def test_on_a_pc_the_preview_is_the_set(self):
|
||||
for model in UD:
|
||||
plan = self.preview(model, "linux")
|
||||
self.assertEqual(plan["file_set_status"], "plausible", plan["issues"])
|
||||
self.assertEqual(plan["candidate_selection"]["model"], model)
|
||||
self.assertNotIn(LINE, plan["messages"])
|
||||
|
||||
def test_an_ist_das_set_on_a_mac_is_still_the_set(self):
|
||||
t = dsv41_sets.tables()["sets"]["IST-DAS-2.25bpw"]["shards"]
|
||||
eng = next(iter(dsv41_sets.tables()["engram"]))
|
||||
with mock.patch.object(models, "_dsv41_files", return_value={**t, eng: dsv41_sets.tables()["engram"][eng]}), \
|
||||
mock.patch.object(sys, "platform", "darwin"), __import__("tempfile").TemporaryDirectory() as tmp:
|
||||
plan = models.plan_import(tmp)
|
||||
self.assertEqual(plan["file_set_status"], "plausible", plan["issues"])
|
||||
self.assertEqual(plan["candidate_selection"]["model"], "IST-DAS-2.25bpw")
|
||||
|
||||
|
||||
class Setup(unittest.TestCase):
|
||||
"""setup.py is the last stop: the line, before an engine, a download or a pack is touched."""
|
||||
|
||||
def run_setup(self, model, platform):
|
||||
import contextlib
|
||||
import importlib.util
|
||||
import io
|
||||
import types
|
||||
root = Path(__file__).resolve().parents[2]
|
||||
spec = importlib.util.spec_from_file_location("setup_mac_ud", root / "setup.py")
|
||||
setup = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(setup)
|
||||
a = types.SimpleNamespace(model=model, models_dir="/nonexistent/models", yes=True, gguf_dir=None)
|
||||
out = io.StringIO()
|
||||
with mock.patch.object(sys, "platform", platform), contextlib.redirect_stdout(out), contextlib.redirect_stderr(out):
|
||||
try:
|
||||
setup.install_dsv41(a, "deepseek41", {}, False, False, 13, True, 8080, 128.0, False)
|
||||
except BaseException as exc: # noqa: BLE001 - fail() exits; anything after the check would raise something else
|
||||
return type(exc).__name__, out.getvalue()
|
||||
return None, out.getvalue()
|
||||
|
||||
def test_a_mac_is_stopped_before_anything_else(self):
|
||||
for model in UD:
|
||||
exc, text = self.run_setup(model, "darwin")
|
||||
self.assertEqual(exc, "SystemExit", (model, text[-400:]))
|
||||
self.assertIn(LINE, text)
|
||||
self.assertIn("IST-DAS-2.25bpw", text)
|
||||
self.assertIn("nothing has been downloaded", text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1338,6 +1338,17 @@ class Strata:
|
||||
"sizes": sizes})
|
||||
return out
|
||||
|
||||
@staticmethod
|
||||
def dsv41_not_on_mac() -> tuple:
|
||||
"""The DeepSeek sets the Metal engine cannot run yet (IQ1_S experts): strata_control/dsv41_fit.MAC_NOT_YET."""
|
||||
from strata_control import dsv41_fit
|
||||
return dsv41_fit.MAC_NOT_YET
|
||||
|
||||
@staticmethod
|
||||
def dsv41_not_on_mac_line() -> str:
|
||||
from strata_control import dsv41_fit
|
||||
return dsv41_fit.MAC_NOT_YET_LINE
|
||||
|
||||
@staticmethod
|
||||
def dsv41_disk_gb(d: dict) -> float:
|
||||
"""The free disk a DeepSeek set needs: its whole download plus the pack and room to work (the card text says "about")."""
|
||||
@@ -2274,6 +2285,8 @@ class Tools:
|
||||
gpu = gpus = kv = low_ram = None
|
||||
if md.get("multi_file"): # DeepSeek on a Mac (0.2.7.1): the sets that were run there, 8K like everywhere, text only
|
||||
from strata_control import dsv41_fit
|
||||
if model in dsv41_fit.MAC_NOT_YET:
|
||||
raise ToolError(dsv41_fit.MAC_NOT_YET_LINE)
|
||||
if model not in dsv41_fit.MAC_SET_PACK:
|
||||
raise ToolError(f"{model} is not offered on a Mac: DeepSeek runs there as " + " or ".join(dsv41_fit.MAC_SET_PACK))
|
||||
if vision in ("yes", "gpu", "cpu"):
|
||||
|
||||
Reference in new issue
Block a user