bleam 0.0.8 → 0.0.9

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.
Files changed (87) hide show
  1. package/dist/cli.cjs +23 -4
  2. package/dist/cli.d.cts +2 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +23 -4
  5. package/dist/{ui-Ce-pnUUA.d.ts → ui-Bg11tvlc.d.ts} +7 -7
  6. package/dist/ui.d.ts +1 -1
  7. package/dist/window.d.ts +1 -1
  8. package/package.json +4 -1
  9. package/templates/image-generation/app/index.tsx +1 -1
  10. package/templates/native/.gitattributes +1 -0
  11. package/templates/native/App.tsx +26 -0
  12. package/templates/native/app.json +12 -0
  13. package/templates/native/index.ts +8 -0
  14. package/templates/native/ios/.xcode.env +11 -0
  15. package/templates/native/ios/Bleam/AI/Flux2/Configuration/Flux2Config.swift +288 -0
  16. package/templates/native/ios/Bleam/AI/Flux2/Configuration/MemoryConfig.swift +233 -0
  17. package/templates/native/ios/Bleam/AI/Flux2/Configuration/MemoryOptimizationConfig.swift +157 -0
  18. package/templates/native/ios/Bleam/AI/Flux2/Configuration/ModelRegistry.swift +425 -0
  19. package/templates/native/ios/Bleam/AI/Flux2/Configuration/QuantizationConfig.swift +138 -0
  20. package/templates/native/ios/Bleam/AI/Flux2/Configuration/TransformerRepoProfile.swift +59 -0
  21. package/templates/native/ios/Bleam/AI/Flux2/Configuration/VAEConfig.swift +134 -0
  22. package/templates/native/ios/Bleam/AI/Flux2/Flux2Core.swift +55 -0
  23. package/templates/native/ios/Bleam/AI/Flux2/Loading/WeightLoader.swift +1192 -0
  24. package/templates/native/ios/Bleam/AI/Flux2/Pipeline/FluxImageRunner.swift +1282 -0
  25. package/templates/native/ios/Bleam/AI/Flux2/Pipeline/LatentUtils.swift +191 -0
  26. package/templates/native/ios/Bleam/AI/Flux2/Scheduler/FlowMatchEulerScheduler.swift +279 -0
  27. package/templates/native/ios/Bleam/AI/Flux2/Text/KleinTextEncoder.swift +380 -0
  28. package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2Attention.swift +379 -0
  29. package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2Embeddings.swift +159 -0
  30. package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2FeedForward.swift +154 -0
  31. package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2FusedKernels.swift +198 -0
  32. package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2KVCache.swift +36 -0
  33. package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2Modulation.swift +193 -0
  34. package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2ParallelAttention.swift +389 -0
  35. package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2RoPE.swift +304 -0
  36. package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2SingleBlock.swift +290 -0
  37. package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2Transformer.swift +530 -0
  38. package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2TransformerBlock.swift +362 -0
  39. package/templates/native/ios/Bleam/AI/Flux2/Utils/Flux2Debug.swift +101 -0
  40. package/templates/native/ios/Bleam/AI/Flux2/Utils/MLXCheckpoint.swift +118 -0
  41. package/templates/native/ios/Bleam/AI/Flux2/Utils/MemoryManager.swift +201 -0
  42. package/templates/native/ios/Bleam/AI/Flux2/VAE/AutoencoderKL.swift +476 -0
  43. package/templates/native/ios/Bleam/AI/Flux2/VAE/ResnetBlock.swift +316 -0
  44. package/templates/native/ios/Bleam/AI/Flux2/VAE/VAEDecoder.swift +120 -0
  45. package/templates/native/ios/Bleam/AI/Flux2/VAE/VAEEncoder.swift +136 -0
  46. package/templates/native/ios/Bleam/AppDelegate.swift +373 -0
  47. package/templates/native/ios/Bleam/Appearance.swift +62 -0
  48. package/templates/native/ios/Bleam/Bridging-Header.h +3 -0
  49. package/templates/native/ios/Bleam/Images.xcassets/AppIcon.appiconset/Contents.json +13 -0
  50. package/templates/native/ios/Bleam/Images.xcassets/Contents.json +6 -0
  51. package/templates/native/ios/Bleam/Info.plist +76 -0
  52. package/templates/native/ios/Bleam/PrivacyInfo.xcprivacy +48 -0
  53. package/templates/native/ios/Bleam/SceneDelegate.swift +58 -0
  54. package/templates/native/ios/Bleam/SplashScreen.storyboard +47 -0
  55. package/templates/native/ios/Bleam/Supporting/Expo.plist +6 -0
  56. package/templates/native/ios/Bleam/bleam.entitlements +10 -0
  57. package/templates/native/ios/Bleam.xcodeproj/project.pbxproj +1146 -0
  58. package/templates/native/ios/Bleam.xcodeproj/xcshareddata/xcschemes/Bleam.xcscheme +88 -0
  59. package/templates/native/ios/GenerationService/GenerationService.entitlements +10 -0
  60. package/templates/native/ios/GenerationService/ImageGenerationRunner.swift +591 -0
  61. package/templates/native/ios/GenerationService/Info.plist +31 -0
  62. package/templates/native/ios/GenerationService/main.swift +165 -0
  63. package/templates/native/ios/PlatformHelper/Info.plist +29 -0
  64. package/templates/native/ios/PlatformHelper/main.swift +335 -0
  65. package/templates/native/ios/Podfile +198 -0
  66. package/templates/native/ios/Podfile.lock +2284 -0
  67. package/templates/native/ios/Podfile.properties.json +5 -0
  68. package/templates/native/ios/Shared/Generation/GenerationServiceProtocol.swift +12 -0
  69. package/templates/native/ios/Shared/Generation/GenerationWorkerProtocol.swift +124 -0
  70. package/templates/native/metro.config.js +6 -0
  71. package/templates/native/modules/bleam-runtime/BleamRuntime.podspec +14 -0
  72. package/templates/native/modules/bleam-runtime/expo-module.config.json +8 -0
  73. package/templates/native/modules/bleam-runtime/ios/AIModule.swift +1258 -0
  74. package/templates/native/modules/bleam-runtime/ios/GenerationContracts.swift +135 -0
  75. package/templates/native/modules/bleam-runtime/ios/PlatformModule.swift +313 -0
  76. package/templates/native/modules/bleam-runtime/package.json +5 -0
  77. package/templates/native/package.json +39 -0
  78. package/templates/native/scripts/build/mlx-frameworks.ts +421 -0
  79. package/templates/native/scripts/start/index.ts +474 -0
  80. package/templates/native/scripts/start/known-failures.ts +20 -0
  81. package/templates/native/scripts/start/loader.ts +76 -0
  82. package/templates/native/scripts/start/native-state.ts +242 -0
  83. package/templates/native/scripts/start/package.json +4 -0
  84. package/templates/native/scripts/start/swift-packages.ts +414 -0
  85. package/templates/native/scripts/start/xcode-formatter.ts +106 -0
  86. package/templates/native/tsconfig.json +14 -0
  87. package/templates/native/yarn.lock +3749 -0
@@ -0,0 +1,425 @@
1
+ import Foundation
2
+
3
+ public enum ModelRegistry {
4
+ public enum TransformerRepo: String, CaseIterable, Sendable {
5
+ case blackForestLabsKlein4B = "black-forest-labs/FLUX.2-klein-4B"
6
+ case moxinOrgKlein4B8bit = "moxin-org/FLUX.2-klein-4B-8bit-mlx"
7
+ case themindstudioKlein4B4bit = "themindstudio/flux2-klein-4b-mlx-4bit"
8
+ // These can be presented as Bonsai models while still using the Flux2 pipeline/runtime.
9
+ case prismMLBonsaiKlein4B2bit = "prism-ml/bonsai-image-ternary-4B-mlx-2bit"
10
+ case themindstudioKlein9B4bit = "themindstudio/flux2-klein-9b-mlx-4bit"
11
+ case aitraderKlein9B8bit = "AITRADER/FLUX2-klein-9B-mlx-8bit"
12
+ case mlxCommunityKlein9B4bit = "mlx-community/FLUX.2-klein-9B"
13
+
14
+ public var profile: Flux2TransformerRepoProfile {
15
+ switch self {
16
+ case .blackForestLabsKlein4B:
17
+ return Flux2TransformerRepoProfile(
18
+ repoId: rawValue,
19
+ model: .klein4B,
20
+ quantization: .bf16,
21
+ relativePathComponents: ["models--black-forest-labs--FLUX.2-klein-4B"],
22
+ requiredSnapshotPatterns: [
23
+ "config.json",
24
+ "model_index.json",
25
+ "flux-2-klein-4b*.safetensors",
26
+ "flux-2-klein-4b*.safetensors.index.json",
27
+ ],
28
+ estimatedSizeGB: 8,
29
+ checkpointFormat: .diffusers
30
+ )
31
+ case .moxinOrgKlein4B8bit:
32
+ return Flux2TransformerRepoProfile(
33
+ repoId: rawValue,
34
+ model: .klein4B,
35
+ quantization: .qint8,
36
+ relativePathComponents: ["models--moxin-org--FLUX.2-klein-4B-8bit-mlx", "transformer"],
37
+ requiredSnapshotPatterns: [
38
+ "transformer/config.json",
39
+ "transformer/quantized_8bit.safetensors",
40
+ ],
41
+ estimatedSizeGB: 4,
42
+ checkpointFormat: .diffusersQuanto
43
+ )
44
+ case .themindstudioKlein4B4bit:
45
+ return Flux2TransformerRepoProfile(
46
+ repoId: rawValue,
47
+ model: .klein4B,
48
+ quantization: .int4,
49
+ relativePathComponents: ["models--themindstudio--flux2-klein-4b-mlx-4bit", "transformer"],
50
+ requiredSnapshotPatterns: [
51
+ "transformer/model.safetensors.index.json",
52
+ "transformer/*.safetensors",
53
+ ],
54
+ estimatedSizeGB: 5,
55
+ checkpointFormat: .packedDiffusers,
56
+ needsNormOutSwap: false,
57
+ usesNormOutBias: true
58
+ )
59
+ case .prismMLBonsaiKlein4B2bit:
60
+ return Flux2TransformerRepoProfile(
61
+ repoId: rawValue,
62
+ model: .klein4B,
63
+ quantization: .int4,
64
+ relativePathComponents: ["models--prism-ml--bonsai-image-ternary-4B-mlx-2bit", "transformer-packed-mflux"],
65
+ requiredSnapshotPatterns: [
66
+ "transformer-packed-mflux/config.json",
67
+ "transformer-packed-mflux/diffusion_pytorch_model.safetensors",
68
+ "transformer-packed-mflux/quantization_config.json",
69
+ ],
70
+ estimatedSizeGB: 3,
71
+ checkpointFormat: .packedDiffusers,
72
+ needsNormOutSwap: false,
73
+ usesNormOutBias: false
74
+ )
75
+ case .themindstudioKlein9B4bit:
76
+ return Flux2TransformerRepoProfile(
77
+ repoId: rawValue,
78
+ model: .klein9B,
79
+ quantization: .int4,
80
+ relativePathComponents: ["models--themindstudio--flux2-klein-9b-mlx-4bit", "transformer"],
81
+ requiredSnapshotPatterns: [
82
+ "transformer/model.safetensors.index.json",
83
+ "transformer/*.safetensors",
84
+ ],
85
+ estimatedSizeGB: 20,
86
+ checkpointFormat: .packedDiffusers,
87
+ needsNormOutSwap: false,
88
+ usesNormOutBias: true
89
+ )
90
+ case .aitraderKlein9B8bit:
91
+ return Flux2TransformerRepoProfile(
92
+ repoId: rawValue,
93
+ model: .klein9B,
94
+ quantization: .qint8,
95
+ relativePathComponents: ["models--AITRADER--FLUX2-klein-9B-mlx-8bit", "transformer"],
96
+ requiredSnapshotPatterns: [
97
+ "transformer/model.safetensors.index.json",
98
+ "transformer/*.safetensors",
99
+ ],
100
+ estimatedSizeGB: 10,
101
+ checkpointFormat: .packedDiffusers,
102
+ needsNormOutSwap: false,
103
+ usesNormOutBias: true
104
+ )
105
+ case .mlxCommunityKlein9B4bit:
106
+ return Flux2TransformerRepoProfile(
107
+ repoId: rawValue,
108
+ model: .klein9B,
109
+ quantization: .int4,
110
+ relativePathComponents: ["models--mlx-community--FLUX.2-klein-9B", "transformer"],
111
+ requiredSnapshotPatterns: [
112
+ "transformer/model.safetensors.index.json",
113
+ "transformer/*.safetensors",
114
+ ],
115
+ estimatedSizeGB: 20,
116
+ checkpointFormat: .packedDiffusers,
117
+ needsNormOutSwap: false,
118
+ usesNormOutBias: true
119
+ )
120
+ }
121
+ }
122
+
123
+ public static func repo(for model: Flux2Model, quantization: TransformerQuantization) -> TransformerRepo {
124
+ switch model {
125
+ case .klein4B:
126
+ switch quantization {
127
+ case .bf16:
128
+ return .blackForestLabsKlein4B
129
+ case .int4:
130
+ return .themindstudioKlein4B4bit
131
+ case .qint8:
132
+ return .moxinOrgKlein4B8bit
133
+ }
134
+ case .klein9B:
135
+ switch quantization {
136
+ case .qint8:
137
+ return .aitraderKlein9B8bit
138
+ case .int4:
139
+ return .themindstudioKlein9B4bit
140
+ case .bf16:
141
+ return .mlxCommunityKlein9B4bit
142
+ }
143
+ }
144
+ }
145
+
146
+ public static func profile(for repoId: String) -> Flux2TransformerRepoProfile? {
147
+ allCases.first { $0.rawValue == repoId }?.profile
148
+ }
149
+ }
150
+
151
+ public enum TransformerVariant: String, CaseIterable, Sendable {
152
+ case klein4B_bf16 = "klein4b-bf16"
153
+ case klein4B_8bit = "klein4b-8bit"
154
+ case klein4B_4bit = "klein4b-4bit"
155
+ case bonsaiKlein4B_2bit = "bonsai-klein4b-2bit"
156
+ case bonsaiKlein4B_1bit = "bonsai-klein4b-1bit"
157
+ case klein9B_8bit = "klein9b-8bit"
158
+ case klein9B_4bit = "klein9b-4bit"
159
+
160
+ public var huggingFaceRepo: String {
161
+ switch self {
162
+ case .klein4B_bf16:
163
+ return "black-forest-labs/FLUX.2-klein-4B"
164
+ case .klein4B_8bit:
165
+ return "moxin-org/FLUX.2-klein-4B-8bit-mlx"
166
+ case .klein4B_4bit:
167
+ return "themindstudio/flux2-klein-4b-mlx-4bit"
168
+ case .bonsaiKlein4B_2bit:
169
+ return "prism-ml/bonsai-image-ternary-4B-mlx-2bit"
170
+ case .bonsaiKlein4B_1bit:
171
+ return "prism-ml/bonsai-image-binary-4B-mlx-1bit"
172
+ case .klein9B_8bit:
173
+ return "AITRADER/FLUX2-klein-9B-mlx-8bit"
174
+ case .klein9B_4bit:
175
+ return "themindstudio/flux2-klein-9b-mlx-4bit"
176
+ }
177
+ }
178
+
179
+ public var huggingFaceSubfolder: String? {
180
+ switch self {
181
+ case .klein4B_bf16:
182
+ return nil
183
+ case .klein4B_8bit, .klein4B_4bit, .bonsaiKlein4B_2bit, .bonsaiKlein4B_1bit, .klein9B_8bit, .klein9B_4bit:
184
+ return "transformer"
185
+ }
186
+ }
187
+
188
+ public var requiredSnapshotPatterns: [String] {
189
+ switch self {
190
+ case .klein4B_bf16:
191
+ return [
192
+ "config.json",
193
+ "model_index.json",
194
+ "flux-2-klein-4b*.safetensors",
195
+ "flux-2-klein-4b*.safetensors.index.json",
196
+ ]
197
+ case .klein4B_8bit:
198
+ return [
199
+ "transformer/config.json",
200
+ "transformer/quantized_8bit.safetensors",
201
+ ]
202
+ case .klein4B_4bit:
203
+ return [
204
+ "transformer/model.safetensors.index.json",
205
+ "transformer/*.safetensors",
206
+ ]
207
+ case .bonsaiKlein4B_2bit:
208
+ return [
209
+ "transformer-packed-mflux/config.json",
210
+ "transformer-packed-mflux/diffusion_pytorch_model.safetensors",
211
+ "transformer-packed-mflux/quantization_config.json",
212
+ ]
213
+ case .bonsaiKlein4B_1bit:
214
+ return [
215
+ "transformer-packed-mflux/config.json",
216
+ "transformer-packed-mflux/diffusion_pytorch_model.safetensors",
217
+ "transformer-packed-mflux/quantization_config.json",
218
+ ]
219
+ case .klein9B_8bit:
220
+ return [
221
+ "transformer/model.safetensors.index.json",
222
+ "transformer/*.safetensors",
223
+ ]
224
+ case .klein9B_4bit:
225
+ return [
226
+ "transformer/model.safetensors.index.json",
227
+ "transformer/*.safetensors",
228
+ ]
229
+ }
230
+ }
231
+
232
+ public var estimatedSizeGB: Int {
233
+ switch self {
234
+ case .klein4B_bf16: return 8
235
+ case .klein4B_8bit: return 4
236
+ case .klein4B_4bit: return 5
237
+ case .bonsaiKlein4B_2bit: return 3
238
+ case .bonsaiKlein4B_1bit: return 2
239
+ case .klein9B_8bit: return 10
240
+ case .klein9B_4bit: return 20
241
+ }
242
+ }
243
+
244
+ public var isGated: Bool { false }
245
+ public var license: String { "Apache 2.0" }
246
+ public var isCommercialUseAllowed: Bool { true }
247
+ public var defaultSteps: Int { 4 }
248
+ public var defaultGuidance: Float { 1.0 }
249
+ public var maxReferenceImages: Int { 4 }
250
+
251
+ public var quantization: TransformerQuantization {
252
+ switch self {
253
+ case .klein4B_bf16: return .bf16
254
+ case .klein4B_8bit: return .qint8
255
+ case .klein4B_4bit: return .int4
256
+ case .bonsaiKlein4B_2bit: return .int4
257
+ case .bonsaiKlein4B_1bit: return .int4
258
+ case .klein9B_8bit: return .qint8
259
+ case .klein9B_4bit: return .int4
260
+ }
261
+ }
262
+
263
+ public var modelType: Flux2Model {
264
+ switch self {
265
+ case .klein4B_bf16, .klein4B_8bit, .klein4B_4bit, .bonsaiKlein4B_2bit, .bonsaiKlein4B_1bit: return .klein4B
266
+ case .klein9B_8bit, .klein9B_4bit: return .klein9B
267
+ }
268
+ }
269
+
270
+ public var isForInference: Bool { true }
271
+ public var isForTraining: Bool { false }
272
+
273
+ public static func variant(for model: Flux2Model, quantization: TransformerQuantization) -> TransformerVariant {
274
+ switch model {
275
+ case .klein4B:
276
+ switch quantization {
277
+ case .bf16: return .klein4B_bf16
278
+ case .qint8: return .klein4B_8bit
279
+ case .int4: return .klein4B_4bit
280
+ }
281
+ case .klein9B:
282
+ switch quantization {
283
+ case .qint8: return .klein9B_8bit
284
+ case .int4: return .klein9B_4bit
285
+ case .bf16: return .klein9B_4bit
286
+ }
287
+ }
288
+ }
289
+ }
290
+
291
+ public enum VAEVariant: String, CaseIterable, Sendable {
292
+ case standard = "standard"
293
+ case smallDecoder = "small-decoder"
294
+ case bonsaiTernary = "bonsai-ternary"
295
+
296
+ public var huggingFaceRepo: String {
297
+ switch self {
298
+ case .standard:
299
+ return "black-forest-labs/FLUX.2-klein-4B"
300
+ case .smallDecoder:
301
+ return "black-forest-labs/FLUX.2-dev"
302
+ case .bonsaiTernary:
303
+ return "prism-ml/bonsai-image-ternary-4B-mlx-2bit"
304
+ }
305
+ }
306
+
307
+ public var huggingFaceSubfolder: String {
308
+ switch self {
309
+ case .standard, .smallDecoder, .bonsaiTernary:
310
+ return "vae"
311
+ }
312
+ }
313
+
314
+ public var requiredSnapshotPatterns: [String] {
315
+ switch self {
316
+ case .standard, .bonsaiTernary:
317
+ return [
318
+ "vae/config.json",
319
+ "vae/diffusion_pytorch_model.safetensors",
320
+ "vae/diffusion_pytorch_model.safetensors.index.json",
321
+ "vae/diffusion_pytorch_model-*.safetensors",
322
+ ]
323
+ case .smallDecoder:
324
+ return [
325
+ "vae/config.json",
326
+ "vae/diffusion_pytorch_model.safetensors",
327
+ "vae/diffusion_pytorch_model.safetensors.index.json",
328
+ "vae/diffusion_pytorch_model-*.safetensors",
329
+ ]
330
+ }
331
+ }
332
+
333
+ public var estimatedSizeGB: Int {
334
+ switch self {
335
+ case .standard, .bonsaiTernary: return 3
336
+ case .smallDecoder: return 1
337
+ }
338
+ }
339
+ public var isGated: Bool { false }
340
+ public var license: String { "Apache 2.0" }
341
+ public var isCommercialUseAllowed: Bool { true }
342
+ }
343
+
344
+ public enum ModelComponent: Hashable, Sendable {
345
+ case transformer(Flux2TransformerRepoProfile)
346
+ case textEncoder
347
+ case vae(VAEVariant)
348
+
349
+ public var displayName: String {
350
+ switch self {
351
+ case .transformer(let profile):
352
+ return "Flux.2 Transformer (\(profile.repoId))"
353
+ case .textEncoder:
354
+ return "Qwen3 Text Encoder"
355
+ case .vae:
356
+ return "Flux.2 VAE"
357
+ }
358
+ }
359
+
360
+ public var estimatedSizeGB: Int {
361
+ switch self {
362
+ case .transformer(let profile): return profile.estimatedSizeGB
363
+ case .textEncoder: return 4
364
+ case .vae(let variant): return variant.estimatedSizeGB
365
+ }
366
+ }
367
+
368
+ public var requiredSnapshotPatterns: [String] {
369
+ switch self {
370
+ case .transformer(let profile):
371
+ return profile.requiredSnapshotPatterns
372
+ case .textEncoder:
373
+ return []
374
+ case .vae(let variant):
375
+ return variant.requiredSnapshotPatterns
376
+ }
377
+ }
378
+ }
379
+
380
+ /// The `models/flux/` subdirectory inside the given container root.
381
+ public static func familyDirectory(containerRoot: URL) -> URL {
382
+ containerRoot
383
+ .appendingPathComponent("models", isDirectory: true)
384
+ .appendingPathComponent("flux", isDirectory: true)
385
+ }
386
+
387
+ /// The `models/flux/LoRAs/` subdirectory inside the given container root.
388
+ public static func loRAsDirectory(containerRoot: URL) -> URL {
389
+ familyDirectory(containerRoot: containerRoot)
390
+ .appendingPathComponent("LoRAs", isDirectory: true)
391
+ }
392
+
393
+ public static func localPath(for component: ModelComponent, containerRoot: URL) -> URL {
394
+ let family = familyDirectory(containerRoot: containerRoot)
395
+ switch component {
396
+ case .transformer(let profile):
397
+ return profile.relativePathComponents.reduce(family) { partial, component in
398
+ partial.appendingPathComponent(component, isDirectory: true)
399
+ }
400
+ case .textEncoder:
401
+ return family
402
+ case .vae(let variant):
403
+ let repoDir = variant.huggingFaceRepo
404
+ .replacingOccurrences(of: "/", with: "--")
405
+ return family
406
+ .appendingPathComponent("models--\(repoDir)")
407
+ .appendingPathComponent("vae")
408
+ }
409
+ }
410
+
411
+ public static func recommendedConfig(forRAMGB ram: Int) -> Flux2QuantizationConfig {
412
+ switch ram {
413
+ case 0..<32:
414
+ return .ultraMinimal
415
+ case 32..<48:
416
+ return .minimal
417
+ case 48..<64:
418
+ return .memoryEfficient
419
+ case 64..<96:
420
+ return .balanced
421
+ default:
422
+ return .highQuality
423
+ }
424
+ }
425
+ }
@@ -0,0 +1,138 @@
1
+ // QuantizationConfig.swift - Fine-grained quantization configuration
2
+ // Copyright 2025 Vincent Gourbin
3
+
4
+ import Foundation
5
+
6
+ /// Quantization level for the Mistral text encoder
7
+ public enum MistralQuantization: String, CaseIterable, Codable, Sendable {
8
+ case bf16 = "bf16" // Full precision ~48GB
9
+ case mlx8bit = "8bit" // 8-bit ~25GB
10
+ case mlx6bit = "6bit" // 6-bit ~19GB
11
+ case mlx4bit = "4bit" // 4-bit ~14GB
12
+
13
+ public var estimatedMemoryGB: Int {
14
+ switch self {
15
+ case .bf16: return 48
16
+ case .mlx8bit: return 25
17
+ case .mlx6bit: return 19
18
+ case .mlx4bit: return 14
19
+ }
20
+ }
21
+
22
+ public var displayName: String {
23
+ switch self {
24
+ case .bf16: return "Full Precision (bf16)"
25
+ case .mlx8bit: return "8-bit"
26
+ case .mlx6bit: return "6-bit"
27
+ case .mlx4bit: return "4-bit"
28
+ }
29
+ }
30
+ }
31
+
32
+ /// Quantization level for the Flux.2 diffusion transformer
33
+ public enum TransformerQuantization: String, CaseIterable, Codable, Sendable {
34
+ case bf16 = "bf16" // Full precision ~64GB
35
+ case qint8 = "qint8" // 8-bit ~32GB
36
+ case int4 = "int4" // 4-bit ~16GB (on-the-fly quantization)
37
+
38
+ public var estimatedMemoryGB: Int {
39
+ switch self {
40
+ case .bf16: return 64
41
+ case .qint8: return 32
42
+ case .int4: return 16
43
+ }
44
+ }
45
+
46
+ public var displayName: String {
47
+ switch self {
48
+ case .bf16: return "Full Precision (bf16)"
49
+ case .qint8: return "8-bit (qint8)"
50
+ case .int4: return "4-bit (int4)"
51
+ }
52
+ }
53
+
54
+ public var bits: Int {
55
+ switch self {
56
+ case .bf16: return 16
57
+ case .qint8: return 8
58
+ case .int4: return 4
59
+ }
60
+ }
61
+
62
+ public var groupSize: Int { 64 }
63
+ }
64
+
65
+ /// Independent quantization configuration for text encoder and transformer
66
+ public struct Flux2QuantizationConfig: Codable, Sendable {
67
+ /// Quantization for the Mistral text encoder
68
+ public var textEncoder: MistralQuantization
69
+
70
+ /// Quantization for the Flux.2 diffusion transformer
71
+ public var transformer: TransformerQuantization
72
+
73
+ public init(
74
+ textEncoder: MistralQuantization,
75
+ transformer: TransformerQuantization
76
+ ) {
77
+ self.textEncoder = textEncoder
78
+ self.transformer = transformer
79
+ }
80
+
81
+ /// Total estimated memory requirement in GB
82
+ public var estimatedTotalMemoryGB: Int {
83
+ // Text encoder and transformer are never loaded simultaneously
84
+ // So we take the max of the two, plus VAE (~3GB) and working memory (~5GB)
85
+ max(textEncoder.estimatedMemoryGB, transformer.estimatedMemoryGB) + 8
86
+ }
87
+
88
+ /// Peak memory during text encoding phase
89
+ public var textEncodingPhaseMemoryGB: Int {
90
+ textEncoder.estimatedMemoryGB + 1 // +1GB for embeddings
91
+ }
92
+
93
+ /// Peak memory during image generation phase
94
+ public var imageGenerationPhaseMemoryGB: Int {
95
+ transformer.estimatedMemoryGB + 3 + 5 // +3GB VAE, +5GB working memory
96
+ }
97
+
98
+ // MARK: - Presets
99
+
100
+ /// High quality preset - requires ~90GB+ RAM
101
+ public static let highQuality = Flux2QuantizationConfig(
102
+ textEncoder: .bf16,
103
+ transformer: .bf16
104
+ )
105
+
106
+ /// Balanced preset - requires ~57GB RAM (recommended for 64GB Macs)
107
+ public static let balanced = Flux2QuantizationConfig(
108
+ textEncoder: .mlx8bit,
109
+ transformer: .qint8
110
+ )
111
+
112
+ /// Memory efficient preset - requires ~47GB RAM
113
+ public static let memoryEfficient = Flux2QuantizationConfig(
114
+ textEncoder: .mlx4bit,
115
+ transformer: .qint8
116
+ )
117
+
118
+ /// Minimal preset - requires ~47GB RAM
119
+ public static let minimal = Flux2QuantizationConfig(
120
+ textEncoder: .mlx4bit,
121
+ transformer: .qint8
122
+ )
123
+
124
+ /// Ultra-minimal preset - requires ~30GB RAM (4-bit transformer)
125
+ public static let ultraMinimal = Flux2QuantizationConfig(
126
+ textEncoder: .mlx4bit,
127
+ transformer: .int4
128
+ )
129
+
130
+ /// Default preset (balanced)
131
+ public static let `default` = balanced
132
+ }
133
+
134
+ extension Flux2QuantizationConfig: CustomStringConvertible {
135
+ public var description: String {
136
+ "Flux2QuantizationConfig(text: \(textEncoder.rawValue), transformer: \(transformer.rawValue), ~\(estimatedTotalMemoryGB)GB)"
137
+ }
138
+ }
@@ -0,0 +1,59 @@
1
+ import Foundation
2
+
3
+ public enum Flux2TransformerCheckpointFormat: String, Sendable {
4
+ case diffusers
5
+ case diffusersQuanto
6
+ case packedDiffusers
7
+ case bfl
8
+ }
9
+
10
+ public struct Flux2TransformerRepoProfile: Hashable, Sendable {
11
+ public let repoId: String
12
+ public let model: Flux2Model
13
+ public let quantization: TransformerQuantization
14
+ public let relativePathComponents: [String]
15
+ public let requiredSnapshotPatterns: [String]
16
+ public let estimatedSizeGB: Int
17
+ public let checkpointFormat: Flux2TransformerCheckpointFormat
18
+ public let needsNormOutSwap: Bool
19
+ public let usesNormOutBias: Bool
20
+
21
+ public init(
22
+ repoId: String,
23
+ model: Flux2Model,
24
+ quantization: TransformerQuantization,
25
+ relativePathComponents: [String],
26
+ requiredSnapshotPatterns: [String],
27
+ estimatedSizeGB: Int,
28
+ checkpointFormat: Flux2TransformerCheckpointFormat,
29
+ needsNormOutSwap: Bool = false,
30
+ usesNormOutBias: Bool = false
31
+ ) {
32
+ self.repoId = repoId
33
+ self.model = model
34
+ self.quantization = quantization
35
+ self.relativePathComponents = relativePathComponents
36
+ self.requiredSnapshotPatterns = requiredSnapshotPatterns
37
+ self.estimatedSizeGB = estimatedSizeGB
38
+ self.checkpointFormat = checkpointFormat
39
+ self.needsNormOutSwap = needsNormOutSwap
40
+ self.usesNormOutBias = usesNormOutBias
41
+ }
42
+
43
+ public func withRuntimeOverrides(
44
+ needsNormOutSwap: Bool? = nil,
45
+ usesNormOutBias: Bool? = nil
46
+ ) -> Flux2TransformerRepoProfile {
47
+ Flux2TransformerRepoProfile(
48
+ repoId: repoId,
49
+ model: model,
50
+ quantization: quantization,
51
+ relativePathComponents: relativePathComponents,
52
+ requiredSnapshotPatterns: requiredSnapshotPatterns,
53
+ estimatedSizeGB: estimatedSizeGB,
54
+ checkpointFormat: checkpointFormat,
55
+ needsNormOutSwap: needsNormOutSwap ?? self.needsNormOutSwap,
56
+ usesNormOutBias: usesNormOutBias ?? self.usesNormOutBias
57
+ )
58
+ }
59
+ }