Commit Graph
70 Commits
Author SHA1 Message Date
aszc-dev 6a0ccbeb60 fix(phase6): make quantize_nbits optional so pre-Phase-6 workflows validate
quantize_nbits was added to the Core ML Converter node's required INPUT_TYPES,
so ComfyUI's /prompt validation rejected (HTTP 400) any workflow saved before
Phase 6 — the field is absent from those prompts. The Tier 2 golden-image test
caught this. Move it to optional: omitted inputs fall back to the convert()
default of "none", so old workflows validate and behave identically while new
users can still opt in. Restores the Gate 6 'existing workflows unaffected'
guarantee.
2026-05-25 17:47:02 +02:00
aszc-dev 0bbd8d8e0d feat(phase6): opt-in k-means weight palettization (quantize_nbits)
Phase 6 of the modernization plan: add weight palettization to the
Core ML converter as an opt-in knob, so the SD1.5 / SDXL UNet can
ship at 1/2, 1/2.7 or 1/4 of its current size with ANE-friendly
inference.

CoreMLConverter (and the LCM converter) gains a `quantize_nbits`
dropdown: `none` (default — identical to pre-Phase-6 behavior and
filenames, so existing cached .mlpackages still resolve) / `8` / `6` /
`4`. The value is encoded as `_q<bits>` after the attn suffix, so the
unquantized model and the three palettized variants coexist on disk
under distinct cache keys.

Implementation
- core/naming.compose_out_name: accepts `quantize_nbits`, validates
  against {none, 8, 6, 4}, appends `_q<bits>` (none = empty).
- converter.convert_unet: after ct.convert + before .save, runs
  coremltools.optimize.coreml.palettize_weights with
  OpPalettizerConfig(mode="kmeans", nbits=...) when the value is not
  "none". Adds a `Palettization took Xs` log line.
- converter.convert / nodes.CoreMLConverter.convert: pipe the new arg
  through; the ComfyUI node exposes it as a dropdown with default
  "none" so existing workflows are unchanged at load time.
- bench/scripts/convert_sd15.py: QUANT_NBITS env knob; uses the
  pure compose_out_name (replaces the inline string formatter).

Test infra
- tests/unit/test_characterization_out_name.py: 6 new tests pinning
  the `_q<bits>` suffix contract, the "none" passthrough (backward
  compat), the cn + lora + quant combination, and the invalid-value
  ValueError. Total Tier 0 now at 94.
- Makefile gains `bench-quant` (runs the matrix script) and
  `convert-quant` (converts q8, q6, q4 sequentially).
- bench/scripts/quant_matrix.py (new): loads each variant, runs
  REPEATS forward passes with a fixed seed, then computes the
  noise_pred PSNR of each quantized variant against the unquantized
  baseline. Writes bench/results/quant_matrix_<sha>.{json,md}.

README
- New "Quantization (Phase 6, opt-in)" section: tradeoff table
  measured on M2 Pro SD1.5 1x512x512 SPLIT_EINSUM (sizes 1641/822/
  617/412 MB; fwd 197/187/183/180 ms; PSNR 53.5 / 40.2 / 27.5 dB),
  plus per-chip/RAM recommendations.

Default-path safety
- "none" produces the same out_name as Phase 5 -> existing
  v1-5-pruned-emaonly_1x512x512_se_unet.mlmodelc is still picked up
  unchanged; the m2 golden image test continues to anchor.
2026-05-25 01:30:29 +02:00
aszc-dev 5dafd261b7 refactor(phase3): split pure logic into coreml_suite.core
Phase 3 of the modernization plan: move the framework-free math out of
the comfy-coupled modules so Tier-0 tests can run on plain Linux without
ComfyUI, coremltools, or python_coreml_stable_diffusion.

New pure-core package (no comfy / coreml / mps imports):
- coreml_suite.core.latents: chunk_batch, merge_chunks
- coreml_suite.core.controlnet: expand_inputs, no_control,
  extract_residual_kwargs, chunk_control
- coreml_suite.core.inputs: CoreMLInputs (chunks + coreml_kwargs)
- coreml_suite.core.sdxl: is_sdxl / is_sdxl_base / is_sdxl_refiner,
  build_sdxl_time_ids (base len 6, refiner len 5), build_sdxl_text_embeds,
  sdxl_model_function_wrapper
- coreml_suite.core.naming: compose_out_name, lora_names_from_params

Thin adapters keep the public import paths:
- coreml_suite.latents / coreml_suite.controlnet: re-export from core
- coreml_suite.models: CoreMLModelWrapper, CoreMLModelWrapperLCM,
  add_sdxl_model_options (now uses the pure builders from core.sdxl),
  get_latent_image, get_model_patcher remain framework-coupled
- coreml_suite.nodes: CoreMLConverter.convert now delegates the out_name
  composition to core.naming.compose_out_name

Test infra:
- tests/unit/* re-pointed at coreml_suite.core.*
- test_chunks.py dropped `from comfy.model_management import ...` and
  the dead `model_config` fixture (Phase 1 left it broken; Phase 3
  removes it entirely)
- test_characterization_sdxl_options now targets the pure builders
  directly via inspect.getclosurevars on the wrapper closure
- test_characterization_out_name now calls compose_out_name without the
  heavy CoreMLConverter monkey-patching that Phase 2 needed
- tests/unit/test_tier0_purity.py: new gate that fails if comfy /
  coremltools / etc leak into sys.modules during a pure `-m unit` run
  (skipped in mixed runs where m2 / integration legitimately import them)
- tests/__init__.py + top-level conftest.py + pyproject addopts
  `--import-mode=importlib --confcutdir=tests` together stop pytest from
  importing the repo-root `__init__.py` (the ComfyUI custom-node entry
  pulls in comfy)
- tests/conftest.py adds tier-aware collect_ignore so `-m unit` skips
  tests/m2 + tests/integration at collection time

Verification:
- `pytest -m unit tests/` → 88 passed in ~2s; deterministic across runs
- Tier-0 purity gate confirms no comfy/coreml/etc in sys.modules
- m2 golden image (Phase 2 anchor) still hashes identical → refactor
  produced bit-for-bit unchanged output
- `git diff main -- __init__.py coreml_suite/nodes.py` shows zero churn
  to NODE_CLASS_MAPPINGS keys or INPUT_TYPES field names (public
  workflow contract intact)
2026-05-22 16:08:01 +02:00
aszc-dev 2fb135df03 Fix set_timestamps for new LCMScheduler implementation 2024-07-04 08:44:37 +02:00
aszc-dev 66e83c2f2f Change syntax to support older Python versions 2024-07-04 08:44:37 +02:00
Chris Chance 7b3f8fc29e Update ModelSamplingDiscreteLCM to Distilled for latest comfyui 2023-12-01 01:09:14 +01:00
Chris Chance adaecd3f66 Lowered minimum CoreML Size to 256x256 2023-11-28 17:49:43 +01:00
aszc-dev 5c774ddc5e Remove LCM option from converter for now 2023-11-24 12:14:15 +01:00
aszc-dev b8197c21ef Converting refiner works 2023-11-24 12:14:15 +01:00
aszc-dev 0c78803b25 Base SDXL conversion works 2023-11-24 12:14:15 +01:00
aszc-dev 763ca3961b Handle SDXL config 2023-11-24 12:14:15 +01:00
aszc-dev ae9a9874c5 Add Advanced Sampler node 2023-11-24 12:14:15 +01:00
aszc-dev 67c902f761 Generating SDXL with Core ML Sampler works 2023-11-24 12:14:15 +01:00
aszc-dev ead01c08dd Remove lora.py 2023-11-17 22:55:13 +01:00
aszc-dev 9f650acb79 Load .yaml config if present 2023-11-17 22:55:13 +01:00
aszc-dev c6d6917827 Setting LoRA model weights works 2023-11-17 22:55:13 +01:00
aszc-dev 63377ebd73 Store lora_params in dict 2023-11-17 22:55:13 +01:00
aszc-dev 42ff10cd43 Add node to load LoRAs 2023-11-17 22:55:13 +01:00
aszc-dev da3a8e13d3 Add logging during conversion 2023-11-17 22:55:13 +01:00
aszc-dev 8092a19173 Enable choosing attention implementation during conversion 2023-11-17 22:55:13 +01:00
aszc-dev 5477e3d71a Remove CLIP loader from nodes 2023-11-17 22:55:13 +01:00
aszc-dev a8d2d6ec46 Move lora related code around, remove clip stuff 2023-11-17 22:55:13 +01:00
aszc-dev 44cffbb8b8 Move load_lora to lora.py 2023-11-17 22:55:13 +01:00
aszc-dev 6907d4910f Remove ckpt loading when loading lora clip 2023-11-17 22:55:13 +01:00
aszc-dev 1930be5c98 Remove CLIP related code 2023-11-17 22:55:13 +01:00
aszc-dev 45be6761d1 Basic conversion + LoRA support works 2023-11-17 22:55:13 +01:00
aszc-dev fc1132a5d5 Fix category for all Core ML nodes 2023-11-17 22:55:13 +01:00
aszc-dev 4a1359b6b5 Negative optional for LCM 2023-11-13 13:18:13 +01:00
aszc-dev 971e60aa09 Rearrange LCM code 2023-11-11 04:15:59 +01:00
aszc-dev 8bcdeab234 Core ML Sampler supports LCM 2023-11-11 03:11:35 +01:00
aszc-dev c9e403b1d8 WIP: LCM Scheduler refactor 2023-11-11 00:19:16 +01:00
aszc-dev 7492f0b486 Extract lcm sampler from lcm sampling node 2023-11-10 13:24:06 +01:00
aszc-dev c09221945d Remove dead code from LCM Sampler 2023-11-10 03:00:42 +01:00
aszc-dev 6864c233e3 ControlNet works for LCM 2023-11-10 02:07:11 +01:00
aszc-dev 6ccf41e5c9 Refactor LCM sampling 2023-11-09 18:02:37 +01:00
aszc-dev 73aa2d11d3 Download scheduler config from repo 2023-11-09 00:07:31 +01:00
aszc-dev 4c438e1ee6 Leverage Comfy's mechanisms to enable LCM ControlNet support 2023-11-09 00:07:30 +01:00
aszc-dev fa0735746c Refactor model config 2023-11-09 00:04:28 +01:00
aszc-dev c26099b334 Add CoreMLInputs to handle inputs 2023-11-08 22:19:41 +01:00
aszc-dev 27f1a19131 Refactor CoreMLModelWrapper 2023-11-08 21:14:55 +01:00
aszc-dev 701443f59e Wrapped Core ML Model is now diffusion_model attribute of BaseModel 2023-11-08 17:51:27 +01:00
aszc-dev bb73e686a0 Add newlines 2023-11-06 23:21:51 +01:00
aszc-dev eeae4bd6e3 Adjust default values for LCM nodes 2023-11-03 01:29:50 +01:00
aszc-dev 1ebd9e72ae Remove Simple LCM Sampler 2023-11-03 01:29:50 +01:00
aszc-dev c01c60e3c1 Add progress bar and preview to LCM 2023-11-03 01:29:50 +01:00
aszc-dev 44a380ffdf img2img works 2023-11-03 01:29:32 +01:00
aszc-dev e22d8187cd Add more advanced LCM Sampler 2023-11-03 01:28:34 +01:00
aszc-dev 1937f39cca Add support for CN models to LCM 2023-11-03 01:27:48 +01:00
aszc-dev b90591dfd4 Add support for controlnet to LCM converter 2023-11-03 01:27:48 +01:00
aszc-dev 1aa5a19b2a Simplify LCM Sampler 2023-11-03 01:27:48 +01:00