coreml-diffusion 0.1.3__tar.gz → 0.1.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- coreml_diffusion-0.1.5/.release-please-manifest.json +3 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/CHANGELOG.md +15 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/PKG-INFO +1 -1
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/__init__.py +17 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/cli.py +12 -5
- coreml_diffusion-0.1.5/coreml_diffusion/conversion/state_dict.py +99 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/convert.py +77 -12
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/inference.py +22 -7
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/pyproject.toml +1 -1
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/smoke/test_coreml_adapters.py +24 -0
- coreml_diffusion-0.1.5/tests/smoke/test_lcm_conversion.py +108 -0
- coreml_diffusion-0.1.5/tests/unit/test_detect_model_version.py +77 -0
- coreml_diffusion-0.1.5/tests/unit/test_state_dict_layout.py +43 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/uv.lock +1 -1
- coreml_diffusion-0.1.3/.release-please-manifest.json +0 -3
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/.github/workflows/publish-pypi.yml +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/.github/workflows/release-please.yml +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/.github/workflows/tier0.yml +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/.github/workflows/tier1.yml +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/.github/workflows/tier2.yml +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/.gitignore +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/LICENSE +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/README.md +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/attention.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/component.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/conversion/__init__.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/conversion/attention.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/conversion/shapes.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/conversion/text_encoder.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/conversion/trace.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/conversion/unet.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/conversion/vae.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/logger.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/model_version.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/naming.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/sources.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/release-please-config.json +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/conftest.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/m2/goldens/sd15_astronaut.png +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/m2/goldens/sd15_astronaut.sha256 +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/m2/goldens/sd15_astronaut_full_coreml.png +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/m2/goldens/sd15_astronaut_full_coreml.sha256 +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/m2/test_inference_golden.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/m2/test_original_gpu.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/smoke/test_original_attention.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/smoke/test_split_einsum_attention.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/smoke/test_synthetic_text_encoder.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/smoke/test_synthetic_unet.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/smoke/test_synthetic_vae.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/unit/test_characterization_component_name.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/unit/test_characterization_out_name.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/unit/test_cli.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/unit/test_conversion_helpers.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/unit/test_discovery_api.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/unit/test_inference_output_contract.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/unit/test_sources.py +0 -0
- {coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/unit/test_tier0_purity.py +0 -0
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.5](https://github.com/aszc-dev/coreml-diffusion/compare/v0.1.4...v0.1.5) (2026-06-13)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### ✨ Features
|
|
7
|
+
|
|
8
|
+
* **convert:** auto-detect model version from the checkpoint ([2a24d4e](https://github.com/aszc-dev/coreml-diffusion/commit/2a24d4efd196100dbdd0bf9d5dd61c6cce31d2ac))
|
|
9
|
+
|
|
10
|
+
## [0.1.4](https://github.com/aszc-dev/coreml-diffusion/compare/v0.1.3...v0.1.4) (2026-06-13)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### 🐛 Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **convert:** generic LCM conversion for arbitrary checkpoints ([bedeb49](https://github.com/aszc-dev/coreml-diffusion/commit/bedeb49a84a33b4530c6c7d4ed4343a914a400e2))
|
|
16
|
+
* **inference:** add output_dtype to CoreMLTextEncoder ([eb6d3b5](https://github.com/aszc-dev/coreml-diffusion/commit/eb6d3b5b08b6ff34a4eb8683de3d1223fb447186))
|
|
17
|
+
|
|
3
18
|
## [0.1.3](https://github.com/aszc-dev/coreml-diffusion/compare/v0.1.2...v0.1.3) (2026-06-04)
|
|
4
19
|
|
|
5
20
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: coreml-diffusion
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.5
|
|
4
4
|
Summary: Convert diffusion-model checkpoints (SD1.5/SDXL) to Core ML for Apple Neural Engine — framework-free, ComfyUI-independent.
|
|
5
5
|
Project-URL: Homepage, https://github.com/aszc-dev/coreml-diffusion
|
|
6
6
|
Project-URL: Repository, https://github.com/aszc-dev/coreml-diffusion
|
|
@@ -44,6 +44,7 @@ __all__ = [
|
|
|
44
44
|
"compose_component_name",
|
|
45
45
|
"lora_names_from_params",
|
|
46
46
|
"convert",
|
|
47
|
+
"detect_model_version",
|
|
47
48
|
"build_pipeline",
|
|
48
49
|
"CoreMLUNet",
|
|
49
50
|
"CoreMLVAE",
|
|
@@ -123,7 +124,23 @@ def __getattr__(name):
|
|
|
123
124
|
if name == "convert":
|
|
124
125
|
from coreml_diffusion.convert import convert as _convert
|
|
125
126
|
|
|
127
|
+
# Importing the submodule binds ``coreml_diffusion.convert`` to the
|
|
128
|
+
# MODULE as a side effect, which shadows this function on every later
|
|
129
|
+
# access (a module object isn't callable). Rebind the package attribute
|
|
130
|
+
# to the function so ``coreml_diffusion.convert(...)`` stays callable in
|
|
131
|
+
# long-lived processes (e.g. a ComfyUI server doing >1 conversion).
|
|
132
|
+
globals()["convert"] = _convert
|
|
126
133
|
return _convert
|
|
134
|
+
if name == "detect_model_version":
|
|
135
|
+
# Lives in the framework-free state_dict module (reads only the
|
|
136
|
+
# safetensors header), so exposing it never drags coremltools/diffusers
|
|
137
|
+
# into the import path.
|
|
138
|
+
from coreml_diffusion.conversion.state_dict import (
|
|
139
|
+
detect_model_version as _detect,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
globals()["detect_model_version"] = _detect
|
|
143
|
+
return _detect
|
|
127
144
|
if name in ("build_pipeline", "CoreMLUNet", "CoreMLVAE", "CoreMLTextEncoder"):
|
|
128
145
|
from coreml_diffusion import inference
|
|
129
146
|
|
|
@@ -33,9 +33,14 @@ def _convert_cmd(args):
|
|
|
33
33
|
sample_size = (args.height // 8, args.width // 8)
|
|
34
34
|
lora_weights = [_parse_lora(spec) for spec in (args.lora or [])]
|
|
35
35
|
ckpt = sources.resolve_checkpoint(args.ckpt, args.source)
|
|
36
|
+
model_version = (
|
|
37
|
+
coreml_diffusion.ModelVersion[args.model_version]
|
|
38
|
+
if args.model_version
|
|
39
|
+
else None
|
|
40
|
+
)
|
|
36
41
|
coreml_diffusion.convert(
|
|
37
42
|
ckpt,
|
|
38
|
-
|
|
43
|
+
model_version,
|
|
39
44
|
args.out,
|
|
40
45
|
component=args.component,
|
|
41
46
|
batch_size=args.batch_size,
|
|
@@ -94,11 +99,13 @@ def build_parser():
|
|
|
94
99
|
)
|
|
95
100
|
conv.add_argument(
|
|
96
101
|
"--model-version",
|
|
97
|
-
|
|
98
|
-
#
|
|
99
|
-
#
|
|
102
|
+
default=None,
|
|
103
|
+
# Auto-detected from the checkpoint when omitted. Choices stay available
|
|
104
|
+
# as an explicit override (the CLI is the power-user path; experimental
|
|
105
|
+
# versions convert but are not golden-verified).
|
|
100
106
|
choices=coreml_diffusion.list_model_versions(include_experimental=True),
|
|
101
|
-
help="Model architecture
|
|
107
|
+
help="Model architecture; auto-detected from the checkpoint when omitted "
|
|
108
|
+
"(verified: SD15, SDXL; experimental otherwise)",
|
|
102
109
|
)
|
|
103
110
|
conv.add_argument(
|
|
104
111
|
"--component",
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""State-dict layout predicates — framework-free (no coremltools/diffusers).
|
|
2
|
+
|
|
3
|
+
Single-file checkpoints come in two layouts: original LDM (UNet keys under
|
|
4
|
+
``model.diffusion_model.``) and diffusers-native UNet-only dumps (block keys at
|
|
5
|
+
the top level — e.g. ``LCM_Dreamshaper_v7_4k.safetensors``, the canonical
|
|
6
|
+
full-distill LCM artifact). diffusers' ``from_single_file`` only understands
|
|
7
|
+
the former and raises ``SingleFileComponentError`` on the latter;
|
|
8
|
+
``convert.load_unet`` routes on this predicate.
|
|
9
|
+
|
|
10
|
+
Also home to ``detect_model_version``: it reads only the safetensors header (no
|
|
11
|
+
coremltools/diffusers), so the conversion entrypoint can auto-pick the model
|
|
12
|
+
version without dragging the heavy stack into the discovery path.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from coreml_diffusion.model_version import ModelVersion
|
|
16
|
+
|
|
17
|
+
DIFFUSERS_UNET_KEY_PREFIXES = ("down_blocks.", "up_blocks.", "mid_block.")
|
|
18
|
+
LDM_UNET_KEY_PREFIX = "model.diffusion_model."
|
|
19
|
+
|
|
20
|
+
# cross_attention_dim -> model version. The context (key/value) dim of the UNet's
|
|
21
|
+
# cross-attention is the architecture fingerprint: SD1.5 conditions on a single
|
|
22
|
+
# 768-dim CLIP, SDXL on the 2048-dim concat of both encoders, the SDXL refiner on
|
|
23
|
+
# the 1280-dim OpenCLIP-bigG alone. A guidance embedding (time_embedding.cond_proj)
|
|
24
|
+
# on top of the 768-dim SD1.5 stack marks a full-distill LCM.
|
|
25
|
+
_CROSS_ATTENTION_DIM_TO_VERSION = {
|
|
26
|
+
768: ModelVersion.SD15,
|
|
27
|
+
2048: ModelVersion.SDXL,
|
|
28
|
+
1280: ModelVersion.SDXL_REFINER,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def is_diffusers_unet_layout(keys) -> bool:
|
|
33
|
+
"""True when ``keys`` form a diffusers-format UNet-only state dict."""
|
|
34
|
+
keys = list(keys)
|
|
35
|
+
has_diffusers_blocks = any(k.startswith(DIFFUSERS_UNET_KEY_PREFIXES) for k in keys)
|
|
36
|
+
has_ldm_prefix = any(k.startswith(LDM_UNET_KEY_PREFIX) for k in keys)
|
|
37
|
+
return has_diffusers_blocks and not has_ldm_prefix
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def safetensors_keys(ckpt_path):
|
|
41
|
+
"""The file's key list when it is safetensors, else None.
|
|
42
|
+
|
|
43
|
+
Probes by content, not filename — a resolved checkpoint path may point at
|
|
44
|
+
an extension-less blob (e.g. inside the Hugging Face cache).
|
|
45
|
+
"""
|
|
46
|
+
from safetensors import SafetensorError, safe_open
|
|
47
|
+
|
|
48
|
+
try:
|
|
49
|
+
with safe_open(ckpt_path, framework="pt") as f:
|
|
50
|
+
return list(f.keys())
|
|
51
|
+
except SafetensorError:
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def detect_model_version(ckpt_path):
|
|
56
|
+
"""Infer the ``ModelVersion`` from a checkpoint's UNet weights.
|
|
57
|
+
|
|
58
|
+
Reads two architecture fingerprints straight from the safetensors header
|
|
59
|
+
(no full model load): the cross-attention context dim (``attn2.to_k``) and
|
|
60
|
+
whether a guidance embedding (``cond_proj``) is present. Works for both LDM
|
|
61
|
+
and diffusers key layouts. Raises ``ValueError`` carrying the observed
|
|
62
|
+
evidence when the architecture is unrecognised, so a bad guess is debuggable
|
|
63
|
+
rather than silent.
|
|
64
|
+
"""
|
|
65
|
+
keys = safetensors_keys(ckpt_path)
|
|
66
|
+
if keys is None:
|
|
67
|
+
raise ValueError(
|
|
68
|
+
f"Cannot auto-detect model version from {ckpt_path!r}: not a readable "
|
|
69
|
+
"safetensors file. Pass model_version explicitly."
|
|
70
|
+
)
|
|
71
|
+
cross_attn_key = next((k for k in keys if k.endswith("attn2.to_k.weight")), None)
|
|
72
|
+
if cross_attn_key is None:
|
|
73
|
+
raise ValueError(
|
|
74
|
+
f"Cannot auto-detect model version from {ckpt_path!r}: no cross-attention "
|
|
75
|
+
"(attn2.to_k) weights found. Pass model_version explicitly."
|
|
76
|
+
)
|
|
77
|
+
from safetensors import safe_open
|
|
78
|
+
|
|
79
|
+
with safe_open(ckpt_path, framework="pt") as f:
|
|
80
|
+
cross_attention_dim = f.get_slice(cross_attn_key).get_shape()[1]
|
|
81
|
+
has_guidance_embedding = any(k.endswith("cond_proj.weight") for k in keys)
|
|
82
|
+
|
|
83
|
+
if has_guidance_embedding:
|
|
84
|
+
if cross_attention_dim == 768:
|
|
85
|
+
return ModelVersion.LCM
|
|
86
|
+
raise ValueError(
|
|
87
|
+
f"Cannot auto-detect model version from {ckpt_path!r}: found a guidance "
|
|
88
|
+
f"embedding (LCM) but cross_attention_dim={cross_attention_dim}; only "
|
|
89
|
+
"SD1.5-class LCM (cross_attention_dim=768) is supported. Pass "
|
|
90
|
+
"model_version explicitly."
|
|
91
|
+
)
|
|
92
|
+
version = _CROSS_ATTENTION_DIM_TO_VERSION.get(cross_attention_dim)
|
|
93
|
+
if version is None:
|
|
94
|
+
raise ValueError(
|
|
95
|
+
f"Cannot auto-detect model version from {ckpt_path!r}: unrecognised "
|
|
96
|
+
f"cross_attention_dim={cross_attention_dim}. Supported: 768 (SD15/LCM), "
|
|
97
|
+
"2048 (SDXL), 1280 (SDXL_REFINER). Pass model_version explicitly."
|
|
98
|
+
)
|
|
99
|
+
return version
|
|
@@ -22,6 +22,11 @@ from coreml_diffusion.attention import ATTENTION_IMPLEMENTATIONS
|
|
|
22
22
|
from coreml_diffusion.component import Component
|
|
23
23
|
from coreml_diffusion.conversion.attention import apply_attention_implementation
|
|
24
24
|
from coreml_diffusion.conversion.shapes import conv2d_output_shape
|
|
25
|
+
from coreml_diffusion.conversion.state_dict import (
|
|
26
|
+
detect_model_version,
|
|
27
|
+
is_diffusers_unet_layout,
|
|
28
|
+
safetensors_keys,
|
|
29
|
+
)
|
|
25
30
|
from coreml_diffusion.conversion.text_encoder import (
|
|
26
31
|
CoreMLTextEncoderWrapper,
|
|
27
32
|
static_causal_mask,
|
|
@@ -139,9 +144,17 @@ def get_sample_input(
|
|
|
139
144
|
return sample_unet_inputs
|
|
140
145
|
|
|
141
146
|
|
|
142
|
-
def lcm_inputs(sample_unet_inputs):
|
|
147
|
+
def lcm_inputs(sample_unet_inputs, ref_unet=None):
|
|
148
|
+
"""Build the LCM guidance-embedding (``timestep_cond``) trace input.
|
|
149
|
+
|
|
150
|
+
The embedding dim comes from ``ref_unet.config.time_cond_proj_dim``. The
|
|
151
|
+
256 fallback preserves the legacy ref-unet-less call shape (the Suite's LCM
|
|
152
|
+
converter calls this with one argument); 256 is correct for every known
|
|
153
|
+
SD1.5 full-distill LCM.
|
|
154
|
+
"""
|
|
143
155
|
batch_size = sample_unet_inputs["sample"].shape[0]
|
|
144
|
-
|
|
156
|
+
dim = 256 if ref_unet is None else ref_unet.config.time_cond_proj_dim
|
|
157
|
+
return {"timestep_cond": torch.randn(batch_size, dim).to(torch.float32)}
|
|
145
158
|
|
|
146
159
|
|
|
147
160
|
def sdxl_inputs(sample_unet_inputs, ref_unet, model_version):
|
|
@@ -257,7 +270,15 @@ def convert_unet(
|
|
|
257
270
|
)
|
|
258
271
|
|
|
259
272
|
if model_version == ModelVersion.LCM:
|
|
260
|
-
|
|
273
|
+
if ref_unet.config.time_cond_proj_dim is None:
|
|
274
|
+
raise ValueError(
|
|
275
|
+
"model_version=LCM requires a UNet with a guidance embedding "
|
|
276
|
+
"(config.time_cond_proj_dim), but this checkpoint has none — "
|
|
277
|
+
"it is an LCM-LoRA merge with plain SD1.5 architecture. "
|
|
278
|
+
"Convert it with model_version=SD15 and use an LCM scheduler "
|
|
279
|
+
"at sampling time."
|
|
280
|
+
)
|
|
281
|
+
sample_inputs |= lcm_inputs(sample_inputs, ref_unet)
|
|
261
282
|
|
|
262
283
|
if model_version in {ModelVersion.SDXL, ModelVersion.SDXL_REFINER}:
|
|
263
284
|
sample_inputs |= sdxl_inputs(sample_inputs, ref_unet, model_version)
|
|
@@ -443,8 +464,8 @@ def convert_text_encoder(
|
|
|
443
464
|
|
|
444
465
|
def convert(
|
|
445
466
|
ckpt_path: str,
|
|
446
|
-
model_version: ModelVersion,
|
|
447
|
-
out_path: str,
|
|
467
|
+
model_version: ModelVersion = None,
|
|
468
|
+
out_path: str = None,
|
|
448
469
|
*,
|
|
449
470
|
component: str = Component.UNET.value,
|
|
450
471
|
batch_size: int = 1,
|
|
@@ -457,20 +478,28 @@ def convert(
|
|
|
457
478
|
):
|
|
458
479
|
"""Convert a single-file checkpoint component to a Core ML ``.mlpackage``.
|
|
459
480
|
|
|
460
|
-
``
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
``
|
|
465
|
-
|
|
466
|
-
|
|
481
|
+
``model_version`` is auto-detected from the checkpoint when left ``None``
|
|
482
|
+
(the architecture fully determines the conversion); pass it explicitly only
|
|
483
|
+
to override a misdetection. ``component`` selects what to convert (default
|
|
484
|
+
``"unet"`` — historical behaviour, all UNet-only kwargs apply).
|
|
485
|
+
``vae_decoder`` / ``vae_encoder`` / ``text_encoder`` / ``text_encoder_2``
|
|
486
|
+
convert the corresponding sub-model and ignore the UNet-only kwargs
|
|
487
|
+
(``controlnet_support``, ``lora_weights``, ``attn_impl``). Keyword-only past
|
|
488
|
+
the leading positionals so the package can add capabilities without breaking
|
|
489
|
+
an older caller. Writes ``out_path``; returns None.
|
|
467
490
|
"""
|
|
491
|
+
if out_path is None:
|
|
492
|
+
raise TypeError("convert() requires out_path")
|
|
468
493
|
if os.path.exists(out_path):
|
|
469
494
|
logger.info(f"Found existing model at {out_path}! Skipping..")
|
|
470
495
|
return
|
|
471
496
|
|
|
472
497
|
comp = Component(component)
|
|
473
498
|
|
|
499
|
+
if model_version is None:
|
|
500
|
+
model_version = detect_model_version(ckpt_path)
|
|
501
|
+
logger.info(f"Auto-detected model version: {model_version.name}")
|
|
502
|
+
|
|
474
503
|
if comp is Component.VAE_DECODER:
|
|
475
504
|
convert_vae_decoder(
|
|
476
505
|
load_vae(ckpt_path),
|
|
@@ -529,12 +558,48 @@ def convert(
|
|
|
529
558
|
|
|
530
559
|
|
|
531
560
|
def load_unet(ckpt_path, config_path):
|
|
561
|
+
"""Load the UNet from a single-file checkpoint, routing on state-dict layout.
|
|
562
|
+
|
|
563
|
+
LDM-layout files (the Civitai norm) go through ``from_single_file``.
|
|
564
|
+
Diffusers-layout UNet-only dumps (the canonical full-distill LCM artifacts,
|
|
565
|
+
e.g. ``LCM_Dreamshaper_v7_4k.safetensors``) are rejected by
|
|
566
|
+
``from_single_file`` outright, so they get a direct state-dict load.
|
|
567
|
+
"""
|
|
568
|
+
keys = safetensors_keys(ckpt_path)
|
|
569
|
+
if keys is not None and is_diffusers_unet_layout(keys):
|
|
570
|
+
return load_unet_from_diffusers_state_dict(ckpt_path)
|
|
532
571
|
return UNet2DConditionModel.from_single_file(
|
|
533
572
|
ckpt_path,
|
|
534
573
|
original_config=config_path,
|
|
535
574
|
)
|
|
536
575
|
|
|
537
576
|
|
|
577
|
+
def load_unet_from_diffusers_state_dict(ckpt_path, **config_overrides):
|
|
578
|
+
"""Load a diffusers-layout UNet-only safetensors dump (SD1.5-class).
|
|
579
|
+
|
|
580
|
+
SD1.5 architecture is assumed (``UNet2DConditionModel`` defaults); the
|
|
581
|
+
cross-attention and guidance-embedding dims are read from the weights, so
|
|
582
|
+
both full-distill LCM dumps (``time_embedding.cond_proj`` present) and
|
|
583
|
+
plain SD1.5 UNet dumps load correctly. A non-SD1.5-class dump fails the
|
|
584
|
+
strict ``load_state_dict`` with an explicit shape/key error.
|
|
585
|
+
``config_overrides`` exists for tests exercising miniature architectures.
|
|
586
|
+
"""
|
|
587
|
+
from safetensors.torch import load_file
|
|
588
|
+
|
|
589
|
+
state_dict = load_file(ckpt_path)
|
|
590
|
+
cond_proj = state_dict.get("time_embedding.cond_proj.weight")
|
|
591
|
+
config_kwargs = {
|
|
592
|
+
"sample_size": 64,
|
|
593
|
+
"cross_attention_dim": state_dict[
|
|
594
|
+
"down_blocks.0.attentions.0.transformer_blocks.0.attn2.to_k.weight"
|
|
595
|
+
].shape[1],
|
|
596
|
+
"time_cond_proj_dim": None if cond_proj is None else cond_proj.shape[1],
|
|
597
|
+
} | config_overrides
|
|
598
|
+
unet = UNet2DConditionModel(**config_kwargs)
|
|
599
|
+
unet.load_state_dict(state_dict)
|
|
600
|
+
return unet
|
|
601
|
+
|
|
602
|
+
|
|
538
603
|
def load_vae(ckpt_path):
|
|
539
604
|
"""Load ``AutoencoderKL`` from a single-file checkpoint."""
|
|
540
605
|
from diffusers import AutoencoderKL
|
|
@@ -268,13 +268,24 @@ class CoreMLTextEncoder(torch.nn.Module):
|
|
|
268
268
|
"""
|
|
269
269
|
|
|
270
270
|
def __init__(
|
|
271
|
-
self,
|
|
271
|
+
self,
|
|
272
|
+
mlpackage_path,
|
|
273
|
+
ref_text_encoder,
|
|
274
|
+
*,
|
|
275
|
+
compute_unit=DEFAULT_COMPUTE_UNIT,
|
|
276
|
+
output_dtype=torch.float16,
|
|
272
277
|
):
|
|
273
278
|
super().__init__()
|
|
274
279
|
import coremltools as ct
|
|
275
280
|
|
|
276
281
|
self.config = ref_text_encoder.config
|
|
277
|
-
|
|
282
|
+
# The package runs fp16 internally. ``output_dtype`` is the dtype of the
|
|
283
|
+
# embeddings handed back to the pipeline; keep fp16 (default) for an
|
|
284
|
+
# all-Core ML pipeline, or set fp32 when the embeddings feed a torch fp32
|
|
285
|
+
# component (e.g. an OAT config with a Core ML text encoder + torch UNet),
|
|
286
|
+
# where diffusers propagates ``prompt_embeds.dtype`` to the latents and time
|
|
287
|
+
# embedding and a Half/Float mismatch would otherwise crash the UNet.
|
|
288
|
+
self.dtype = output_dtype
|
|
278
289
|
unit = _resolve_unit(compute_unit)
|
|
279
290
|
logger.info(f"Loading text encoder {mlpackage_path} to {unit.name}")
|
|
280
291
|
self.model = ct.models.MLModel(mlpackage_path, compute_units=unit)
|
|
@@ -294,14 +305,18 @@ class CoreMLTextEncoder(torch.nn.Module):
|
|
|
294
305
|
**_ignored,
|
|
295
306
|
):
|
|
296
307
|
prediction = self.model.predict({"input_ids": _i32(input_ids)})
|
|
297
|
-
embeds =
|
|
298
|
-
|
|
308
|
+
embeds = (
|
|
309
|
+
torch.from_numpy(np.ascontiguousarray(prediction["hidden_states"]))
|
|
310
|
+
.to(input_ids.device)
|
|
311
|
+
.to(self.dtype)
|
|
299
312
|
)
|
|
300
313
|
pooled = None
|
|
301
314
|
if self._pooled_name is not None:
|
|
302
|
-
pooled =
|
|
303
|
-
np.ascontiguousarray(prediction[self._pooled_name])
|
|
304
|
-
|
|
315
|
+
pooled = (
|
|
316
|
+
torch.from_numpy(np.ascontiguousarray(prediction[self._pooled_name]))
|
|
317
|
+
.to(input_ids.device)
|
|
318
|
+
.to(self.dtype)
|
|
319
|
+
)
|
|
305
320
|
return _CoreMLTextEncoderOutput(embeds, pooled)
|
|
306
321
|
|
|
307
322
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "coreml-diffusion"
|
|
3
3
|
description = "Convert diffusion-model checkpoints (SD1.5/SDXL) to Core ML for Apple Neural Engine — framework-free, ComfyUI-independent."
|
|
4
|
-
version = "0.1.
|
|
4
|
+
version = "0.1.5"
|
|
5
5
|
license = "MIT"
|
|
6
6
|
license-files = ["LICENSE"]
|
|
7
7
|
requires-python = ">=3.12,<3.13"
|
|
@@ -118,6 +118,30 @@ def test_coreml_text_encoder_sd15_shape(tmp_path):
|
|
|
118
118
|
|
|
119
119
|
assert out[0].shape == (1, SEQ_LEN, HIDDEN) # no pooled -> [0] is embeds
|
|
120
120
|
assert out.hidden_states[-2].shape == (1, SEQ_LEN, HIDDEN)
|
|
121
|
+
assert out[0].dtype == torch.float16 # default: fp16 for an all-Core ML pipeline
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def test_coreml_text_encoder_output_dtype_fp32(tmp_path):
|
|
125
|
+
# output_dtype=fp32 bridges a Core ML text encoder feeding a torch fp32 UNet
|
|
126
|
+
# (an OAT config): the embeddings must come back fp32 so diffusers does not
|
|
127
|
+
# propagate fp16 into the fp32 UNet and crash on a Half/Float mismatch.
|
|
128
|
+
from transformers import CLIPTextModel
|
|
129
|
+
|
|
130
|
+
from coreml_diffusion.inference import CoreMLTextEncoder
|
|
131
|
+
|
|
132
|
+
torch.manual_seed(0)
|
|
133
|
+
encoder = CLIPTextModel(_tiny_clip_config())
|
|
134
|
+
pkg = tmp_path / "text_encoder.mlpackage"
|
|
135
|
+
_convert_text_encoder(encoder, ["hidden_states"], pkg, index=None, pooled=False)
|
|
136
|
+
|
|
137
|
+
coreml_te = CoreMLTextEncoder(
|
|
138
|
+
str(pkg), encoder, compute_unit="CPU_ONLY", output_dtype=torch.float32
|
|
139
|
+
)
|
|
140
|
+
out = coreml_te(torch.zeros(1, SEQ_LEN, dtype=torch.long))
|
|
141
|
+
|
|
142
|
+
assert coreml_te.dtype == torch.float32
|
|
143
|
+
assert out[0].dtype == torch.float32
|
|
144
|
+
assert out.last_hidden_state.dtype == torch.float32
|
|
121
145
|
|
|
122
146
|
|
|
123
147
|
def test_coreml_text_encoder_sdxl_pooled(tmp_path):
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Tier 1 smoke: the generic LCM conversion path on a synthetic micro-UNet.
|
|
2
|
+
|
|
3
|
+
Locks the E-LCM fixes: the ``timestep_cond`` trace input takes its dim from
|
|
4
|
+
``config.time_cond_proj_dim`` (not a hardcoded 256), an LCM-LoRA merge (no
|
|
5
|
+
guidance embedding) is rejected with a clear error, and diffusers-layout
|
|
6
|
+
UNet-only dumps (the canonical full-distill LCM artifact shape) load through
|
|
7
|
+
``load_unet_from_diffusers_state_dict``.
|
|
8
|
+
|
|
9
|
+
Auto-skips on non-Apple-Silicon hosts so Tier 0 CI on Linux ignores it.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import platform
|
|
13
|
+
|
|
14
|
+
import pytest
|
|
15
|
+
import torch
|
|
16
|
+
|
|
17
|
+
pytestmark = pytest.mark.skipif(
|
|
18
|
+
platform.system() != "Darwin" or platform.machine() != "arm64",
|
|
19
|
+
reason="Tier 1 requires macOS on Apple Silicon",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
GUIDANCE_DIM = 16 # deliberately != 256 to prove the dim comes from the config
|
|
23
|
+
|
|
24
|
+
TINY_ARCH = dict(
|
|
25
|
+
sample_size=8,
|
|
26
|
+
layers_per_block=1,
|
|
27
|
+
block_out_channels=(32, 64),
|
|
28
|
+
down_block_types=("CrossAttnDownBlock2D", "DownBlock2D"),
|
|
29
|
+
up_block_types=("UpBlock2D", "CrossAttnUpBlock2D"),
|
|
30
|
+
cross_attention_dim=32,
|
|
31
|
+
attention_head_dim=8,
|
|
32
|
+
norm_num_groups=8,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _tiny_unet(time_cond_proj_dim=None):
|
|
37
|
+
from diffusers import UNet2DConditionModel
|
|
38
|
+
|
|
39
|
+
return UNet2DConditionModel(**TINY_ARCH, time_cond_proj_dim=time_cond_proj_dim)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_lcm_unet_converts_with_config_guidance_dim(tmp_path):
|
|
43
|
+
import coremltools as ct
|
|
44
|
+
|
|
45
|
+
from coreml_diffusion.convert import convert_unet
|
|
46
|
+
from coreml_diffusion.model_version import ModelVersion
|
|
47
|
+
|
|
48
|
+
torch.manual_seed(0)
|
|
49
|
+
out_path = str(tmp_path / "lcm_unet.mlpackage")
|
|
50
|
+
convert_unet(
|
|
51
|
+
_tiny_unet(time_cond_proj_dim=GUIDANCE_DIM),
|
|
52
|
+
ModelVersion.LCM,
|
|
53
|
+
out_path,
|
|
54
|
+
sample_size=(8, 8),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
spec = ct.models.MLModel(out_path, skip_model_load=True).get_spec()
|
|
58
|
+
inputs = {
|
|
59
|
+
i.name: tuple(i.type.multiArrayType.shape) for i in spec.description.input
|
|
60
|
+
}
|
|
61
|
+
assert "timestep_cond" in inputs
|
|
62
|
+
assert inputs["timestep_cond"] == (1, GUIDANCE_DIM)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_lcm_merge_checkpoint_is_rejected(tmp_path):
|
|
66
|
+
from coreml_diffusion.convert import convert_unet
|
|
67
|
+
from coreml_diffusion.model_version import ModelVersion
|
|
68
|
+
|
|
69
|
+
torch.manual_seed(0)
|
|
70
|
+
with pytest.raises(ValueError, match="LCM-LoRA merge"):
|
|
71
|
+
convert_unet(
|
|
72
|
+
_tiny_unet(time_cond_proj_dim=None),
|
|
73
|
+
ModelVersion.LCM,
|
|
74
|
+
str(tmp_path / "merge.mlpackage"),
|
|
75
|
+
sample_size=(8, 8),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def test_diffusers_layout_dump_loads_with_guidance_embedding(tmp_path):
|
|
80
|
+
from safetensors.torch import save_file
|
|
81
|
+
|
|
82
|
+
from coreml_diffusion.convert import load_unet_from_diffusers_state_dict
|
|
83
|
+
|
|
84
|
+
torch.manual_seed(0)
|
|
85
|
+
source = _tiny_unet(time_cond_proj_dim=GUIDANCE_DIM)
|
|
86
|
+
dump = tmp_path / "tiny_lcm_dump.safetensors"
|
|
87
|
+
save_file(source.state_dict(), str(dump))
|
|
88
|
+
|
|
89
|
+
# cross_attention_dim and time_cond_proj_dim must be read from the weights;
|
|
90
|
+
# only the miniature architecture is supplied as overrides.
|
|
91
|
+
loaded = load_unet_from_diffusers_state_dict(
|
|
92
|
+
str(dump), **{k: v for k, v in TINY_ARCH.items() if k != "cross_attention_dim"}
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
assert loaded.config.cross_attention_dim == TINY_ARCH["cross_attention_dim"]
|
|
96
|
+
assert loaded.config.time_cond_proj_dim == GUIDANCE_DIM
|
|
97
|
+
assert torch.equal(
|
|
98
|
+
loaded.time_embedding.cond_proj.weight, source.time_embedding.cond_proj.weight
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_lcm_inputs_legacy_call_keeps_256():
|
|
103
|
+
# The Suite's LCM converter calls lcm_inputs without a ref UNet; the legacy
|
|
104
|
+
# 256 fallback is part of the import contract until the Suite migrates.
|
|
105
|
+
from coreml_diffusion.convert import lcm_inputs
|
|
106
|
+
|
|
107
|
+
inputs = lcm_inputs({"sample": torch.rand(2, 4, 8, 8)})
|
|
108
|
+
assert inputs["timestep_cond"].shape == (2, 256)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Tier 0: model-version auto-detection from checkpoint weights.
|
|
2
|
+
|
|
3
|
+
Locks the architecture fingerprinting that lets ``convert(model_version=None)``
|
|
4
|
+
pick the right conversion path: cross-attention context dim (attn2.to_k) plus
|
|
5
|
+
the presence of a guidance embedding (cond_proj). Synthetic safetensors files
|
|
6
|
+
carry only the two keys the detector reads, so the test stays framework-free.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
import torch
|
|
11
|
+
from safetensors.torch import save_file
|
|
12
|
+
|
|
13
|
+
from coreml_diffusion.conversion.state_dict import detect_model_version
|
|
14
|
+
from coreml_diffusion.model_version import ModelVersion
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _write_ckpt(path, cross_attention_dim, *, guidance=False, with_cross_attn=True):
|
|
18
|
+
tensors = {}
|
|
19
|
+
if with_cross_attn:
|
|
20
|
+
# attn2.to_k maps the context dim -> inner dim; shape[1] is what we read.
|
|
21
|
+
key = "down_blocks.0.attentions.0.transformer_blocks.0.attn2.to_k.weight"
|
|
22
|
+
tensors[key] = torch.zeros(320, cross_attention_dim)
|
|
23
|
+
if guidance:
|
|
24
|
+
tensors["time_embedding.cond_proj.weight"] = torch.zeros(320, 256)
|
|
25
|
+
if not tensors:
|
|
26
|
+
tensors["dummy"] = torch.zeros(1)
|
|
27
|
+
save_file(tensors, str(path))
|
|
28
|
+
return str(path)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.mark.parametrize(
|
|
32
|
+
"cross_attention_dim, guidance, expected",
|
|
33
|
+
[
|
|
34
|
+
(768, False, ModelVersion.SD15),
|
|
35
|
+
(768, True, ModelVersion.LCM),
|
|
36
|
+
(2048, False, ModelVersion.SDXL),
|
|
37
|
+
(1280, False, ModelVersion.SDXL_REFINER),
|
|
38
|
+
],
|
|
39
|
+
)
|
|
40
|
+
def test_detects_known_architectures(tmp_path, cross_attention_dim, guidance, expected):
|
|
41
|
+
ckpt = _write_ckpt(
|
|
42
|
+
tmp_path / "m.safetensors", cross_attention_dim, guidance=guidance
|
|
43
|
+
)
|
|
44
|
+
assert detect_model_version(ckpt) is expected
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_lcm_lora_merge_detects_as_sd15(tmp_path):
|
|
48
|
+
# An LCM-LoRA merge is plain SD1.5 architecture (no guidance embedding); it
|
|
49
|
+
# must NOT be mistaken for a full-distill LCM.
|
|
50
|
+
ckpt = _write_ckpt(tmp_path / "merge.safetensors", 768, guidance=False)
|
|
51
|
+
assert detect_model_version(ckpt) is ModelVersion.SD15
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_guidance_with_non_sd15_dim_is_rejected(tmp_path):
|
|
55
|
+
ckpt = _write_ckpt(tmp_path / "sdxl_lcm.safetensors", 2048, guidance=True)
|
|
56
|
+
with pytest.raises(ValueError, match="only SD1.5-class LCM"):
|
|
57
|
+
detect_model_version(ckpt)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_unknown_cross_attention_dim_is_rejected(tmp_path):
|
|
61
|
+
# 1024 is SD2.x — unsupported; the error must name the observed dim.
|
|
62
|
+
ckpt = _write_ckpt(tmp_path / "sd2.safetensors", 1024)
|
|
63
|
+
with pytest.raises(ValueError, match="cross_attention_dim=1024"):
|
|
64
|
+
detect_model_version(ckpt)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_no_cross_attention_weights_is_rejected(tmp_path):
|
|
68
|
+
ckpt = _write_ckpt(tmp_path / "weird.safetensors", 768, with_cross_attn=False)
|
|
69
|
+
with pytest.raises(ValueError, match="no cross-attention"):
|
|
70
|
+
detect_model_version(ckpt)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_non_safetensors_is_rejected(tmp_path):
|
|
74
|
+
bogus = tmp_path / "model.ckpt"
|
|
75
|
+
bogus.write_bytes(b"not safetensors")
|
|
76
|
+
with pytest.raises(ValueError, match="not a readable safetensors"):
|
|
77
|
+
detect_model_version(str(bogus))
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Tier 0: state-dict layout detection routing ``load_unet``.
|
|
2
|
+
|
|
3
|
+
Locks the predicate that decides whether a single-file checkpoint is an LDM
|
|
4
|
+
checkpoint (``from_single_file`` path) or a diffusers-layout UNet-only dump
|
|
5
|
+
(direct state-dict load — the canonical full-distill LCM artifact shape).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from coreml_diffusion.conversion.state_dict import is_diffusers_unet_layout
|
|
9
|
+
|
|
10
|
+
DIFFUSERS_UNET_KEYS = [
|
|
11
|
+
"conv_in.weight",
|
|
12
|
+
"time_embedding.linear_1.weight",
|
|
13
|
+
"time_embedding.cond_proj.weight",
|
|
14
|
+
"down_blocks.0.attentions.0.transformer_blocks.0.attn2.to_k.weight",
|
|
15
|
+
"mid_block.resnets.0.conv1.weight",
|
|
16
|
+
"up_blocks.3.resnets.2.conv2.weight",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
LDM_CHECKPOINT_KEYS = [
|
|
20
|
+
"model.diffusion_model.time_embed.0.weight",
|
|
21
|
+
"model.diffusion_model.input_blocks.0.0.weight",
|
|
22
|
+
"first_stage_model.decoder.conv_in.weight",
|
|
23
|
+
"cond_stage_model.transformer.text_model.embeddings.token_embedding.weight",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_diffusers_unet_dump_is_detected():
|
|
28
|
+
assert is_diffusers_unet_layout(DIFFUSERS_UNET_KEYS)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_ldm_checkpoint_is_not_diffusers_layout():
|
|
32
|
+
assert not is_diffusers_unet_layout(LDM_CHECKPOINT_KEYS)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_mixed_prefixes_resolve_to_ldm():
|
|
36
|
+
# An LDM file whose extras coincidentally include diffusers-looking keys
|
|
37
|
+
# must still go through from_single_file.
|
|
38
|
+
assert not is_diffusers_unet_layout(LDM_CHECKPOINT_KEYS + DIFFUSERS_UNET_KEYS)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_unrelated_keys_are_not_diffusers_layout():
|
|
42
|
+
assert not is_diffusers_unet_layout(["text_model.encoder.layers.0.mlp.fc1.weight"])
|
|
43
|
+
assert not is_diffusers_unet_layout([])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/coreml_diffusion/conversion/text_encoder.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/m2/goldens/sd15_astronaut_full_coreml.png
RENAMED
|
File without changes
|
{coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/m2/goldens/sd15_astronaut_full_coreml.sha256
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/smoke/test_split_einsum_attention.py
RENAMED
|
File without changes
|
{coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/smoke/test_synthetic_text_encoder.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/unit/test_characterization_component_name.py
RENAMED
|
File without changes
|
{coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/unit/test_characterization_out_name.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{coreml_diffusion-0.1.3 → coreml_diffusion-0.1.5}/tests/unit/test_inference_output_contract.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|