bleam 0.0.7 → 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 (92) 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-234Plg7Z.d.cts → 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
  88. package/dist/ui-BJmXhz9Q.d.ts +0 -92
  89. package/dist/ui-Dd7SXdbg.d.cts +0 -92
  90. package/dist/ui-Ds88eETu.d.ts +0 -92
  91. package/dist/ui-TaqnB5SP.d.ts +0 -92
  92. package/dist/ui-WQvnXL0W.d.ts +0 -92
@@ -0,0 +1,198 @@
1
+ // Flux2FusedKernels.swift - Custom Metal kernels for performance-critical operations
2
+ // Copyright 2025 Vincent Gourbin
3
+ //
4
+ // The RoPE kernel implementation is inspired by mzbac/flux2.swift
5
+ // (https://github.com/mzbac/flux2.swift), licensed under Apache 2.0.
6
+ // This implementation has been adapted for [B, H, S, D] tensor layout
7
+ // (original uses [B, S, H, D]) and integrated with our caching system.
8
+
9
+ import Foundation
10
+ import MLX
11
+
12
+ /// Collection of fused Metal kernels for performance-critical transformer operations
13
+ ///
14
+ /// These kernels fuse multiple operations into a single GPU dispatch, reducing
15
+ /// kernel launch overhead and improving memory locality.
16
+ public enum Flux2FusedKernels {
17
+ /// Set to true to disable fused kernels (for debugging/comparison)
18
+ /// Can also be set via environment variable FLUX2_DISABLE_FUSED_KERNELS=1
19
+ nonisolated(unsafe) public static var disabled: Bool = {
20
+ ProcessInfo.processInfo.environment["FLUX2_DISABLE_FUSED_KERNELS"] == "1"
21
+ }()
22
+ /// Number of attention heads processed per thread for RoPE kernel
23
+ private static let ropeHeadsPerThread = 4
24
+
25
+ /// Custom Metal kernel for applying rotary position embeddings
26
+ ///
27
+ /// This kernel fuses the rotation operation (x * cos + rotated_x * sin) into a single
28
+ /// GPU kernel, processing multiple heads per thread for efficiency.
29
+ ///
30
+ /// Input layout: [B, H, S, D] where B=batch, H=heads, S=sequence, D=head_dim
31
+ /// Cos/Sin layout: [S, D] or [B, S, D]
32
+ private static let ropeKernel = MLXFast.metalKernel(
33
+ name: "flux2_apply_rope_bhsd",
34
+ inputNames: ["x", "cos", "sin", "batch", "heads", "seq", "head_dim", "rotary_batch_stride"],
35
+ outputNames: ["out"],
36
+ source: """
37
+ uint dp = thread_position_in_grid.x; // dimension pair index (0..half_dim-1)
38
+ uint seq_idx = thread_position_in_grid.y; // sequence position
39
+ uint z = thread_position_in_grid.z; // batch * head_groups
40
+
41
+ uint half_dim = uint(head_dim / 2);
42
+ if (dp >= half_dim || seq_idx >= uint(seq)) {
43
+ return;
44
+ }
45
+
46
+ int head_groups = (heads + (N - 1)) / N;
47
+ int batch_idx = int(z) / head_groups;
48
+ int head_group = int(z) - batch_idx * head_groups;
49
+ if (batch_idx >= batch) {
50
+ return;
51
+ }
52
+
53
+ int head_base = head_group * N;
54
+
55
+ // cos/sin layout: [S, D] if rotary_batch_stride=0, else [B, S, D]
56
+ ulong row_base = (ulong(seq_idx) + ulong(batch_idx) * ulong(rotary_batch_stride)) * ulong(head_dim);
57
+ device const float* cos_row = cos + row_base;
58
+ device const float* sin_row = sin + row_base;
59
+
60
+ uint d0 = dp * 2;
61
+ float c = cos_row[d0];
62
+ float s = sin_row[d0];
63
+
64
+ // For [B, H, S, D] layout:
65
+ // x[batch][head][seq][dim] = x[batch * H * S * D + head * S * D + seq * D + dim]
66
+ ulong stride_S = ulong(head_dim);
67
+ ulong stride_H = ulong(seq) * stride_S;
68
+ ulong stride_B = ulong(heads) * stride_H;
69
+
70
+ for (int i = 0; i < N; ++i) {
71
+ int head_idx = head_base + i;
72
+ if (head_idx >= heads) {
73
+ break;
74
+ }
75
+
76
+ ulong base = ulong(batch_idx) * stride_B + ulong(head_idx) * stride_H + ulong(seq_idx) * stride_S + ulong(d0);
77
+ float x0 = float(x[base]);
78
+ float x1 = float(x[base + 1]);
79
+
80
+ // Apply rotation: [x0, x1] * [[c, -s], [s, c]]
81
+ // Result: [x0*c - x1*s, x0*s + x1*c]
82
+ // But diffusers uses: x * cos + rotate_half(x) * sin
83
+ // where rotate_half gives [-x1, x0] for consecutive pairs
84
+ // So: [x0, x1] -> [x0*c - x1*s, x1*c + x0*s]
85
+ out[base] = T(x0 * c - x1 * s);
86
+ out[base + 1] = T(x1 * c + x0 * s);
87
+ }
88
+ """
89
+ )
90
+
91
+ /// Apply rotary embeddings using the fused Metal kernel
92
+ ///
93
+ /// - Parameters:
94
+ /// - x: Input tensor [B, H, S, D] in bfloat16, float16, or float32
95
+ /// - cos: Cosine embeddings [S, D] or [B, S, D] in float32
96
+ /// - sin: Sine embeddings [S, D] or [B, S, D] in float32
97
+ /// - Returns: Rotated tensor [B, H, S, D] in same dtype as input, or nil if kernel can't be used
98
+ public static func applyRotaryEmb(
99
+ _ x: MLXArray,
100
+ cos: MLXArray,
101
+ sin: MLXArray
102
+ ) -> MLXArray? {
103
+ // Allow disabling for debugging
104
+ guard !disabled else { return nil }
105
+
106
+ // Only use kernel on GPU
107
+ guard Device.defaultDevice().deviceType == .gpu else {
108
+ return nil
109
+ }
110
+
111
+ // Validate dtypes
112
+ guard x.dtype == .bfloat16 || x.dtype == .float16 || x.dtype == .float32,
113
+ cos.dtype == .float32,
114
+ sin.dtype == .float32
115
+ else {
116
+ return nil
117
+ }
118
+
119
+ // Validate x shape: [B, H, S, D]
120
+ guard x.ndim == 4 else {
121
+ return nil
122
+ }
123
+
124
+ let batch = x.dim(0)
125
+ let heads = x.dim(1)
126
+ let seq = x.dim(2)
127
+ let headDim = x.dim(3)
128
+
129
+ // Validate cos/sin shape: [S, D] or [B, S, D]
130
+ guard (cos.ndim == 2 || cos.ndim == 3), cos.ndim == sin.ndim else { return nil }
131
+
132
+ let batchStride: Int32
133
+ if cos.ndim == 2 {
134
+ // [S, D] - shared across batch
135
+ guard cos.dim(0) == seq, sin.dim(0) == seq else { return nil }
136
+ guard cos.dim(1) == headDim, sin.dim(1) == headDim else { return nil }
137
+ batchStride = 0
138
+ } else if cos.ndim == 3 {
139
+ // [B, S, D] - per-batch embeddings
140
+ guard cos.dim(0) == batch, sin.dim(0) == batch else { return nil }
141
+ guard cos.dim(1) == seq, sin.dim(1) == seq else { return nil }
142
+ guard cos.dim(2) == headDim, sin.dim(2) == headDim else { return nil }
143
+ batchStride = Int32(seq)
144
+ } else {
145
+ return nil
146
+ }
147
+
148
+ // Head dimension must be even for rotation
149
+ guard headDim % 2 == 0 else {
150
+ return nil
151
+ }
152
+
153
+ let halfDim = headDim / 2
154
+ let headsPerThread = ropeHeadsPerThread
155
+ let headGroups = (heads + headsPerThread - 1) / headsPerThread
156
+
157
+ // Thread group sizing
158
+ let tgX = max(min(32, halfDim), 1)
159
+ let tgY = max(min(4, seq), 1)
160
+
161
+ let out = ropeKernel(
162
+ [
163
+ x,
164
+ cos,
165
+ sin,
166
+ Int32(batch),
167
+ Int32(heads),
168
+ Int32(seq),
169
+ Int32(headDim),
170
+ batchStride,
171
+ ],
172
+ template: [
173
+ ("T", x.dtype),
174
+ ("N", headsPerThread),
175
+ ],
176
+ grid: (halfDim, seq, batch * headGroups),
177
+ threadGroup: (tgX, tgY, 1),
178
+ outputShapes: [x.shape],
179
+ outputDTypes: [x.dtype],
180
+ stream: .gpu
181
+ )
182
+ return out[0]
183
+ }
184
+
185
+ /// Check if the fused RoPE kernel is available for the given configuration
186
+ public static func isRopeKernelAvailable(
187
+ xDtype: DType,
188
+ xNdim: Int,
189
+ cosNdim: Int,
190
+ sinNdim: Int
191
+ ) -> Bool {
192
+ guard Device.defaultDevice().deviceType == .gpu else { return false }
193
+ guard xDtype == .bfloat16 || xDtype == .float16 || xDtype == .float32 else { return false }
194
+ guard xNdim == 4 else { return false }
195
+ guard (cosNdim == 2 || cosNdim == 3), cosNdim == sinNdim else { return false }
196
+ return true
197
+ }
198
+ }
@@ -0,0 +1,36 @@
1
+ // Flux2KVCache.swift - KV Cache for Klein 9B acceleration
2
+ // Copyright 2025 Vincent Gourbin
3
+
4
+ import Foundation
5
+ import MLX
6
+
7
+ /// KV Cache for a single attention layer
8
+ /// Stores the key and value projections for reference tokens
9
+ public struct Flux2KVCacheLayer {
10
+ public let k: MLXArray // [B, numHeads, refSeqLen, headDim]
11
+ public let v: MLXArray // [B, numHeads, refSeqLen, headDim]
12
+
13
+ public init(k: MLXArray, v: MLXArray) {
14
+ self.k = k
15
+ self.v = v
16
+ }
17
+ }
18
+
19
+ /// Complete KV Cache for all transformer layers
20
+ /// Used to skip reference token re-processing after step 0
21
+ public struct Flux2KVCache {
22
+ public let doubleStreamLayers: [Flux2KVCacheLayer] // 8 layers
23
+ public let singleStreamLayers: [Flux2KVCacheLayer] // 48 layers
24
+ public let layerCount: Int
25
+
26
+ public init(doubleStreamLayers: [Flux2KVCacheLayer], singleStreamLayers: [Flux2KVCacheLayer]) {
27
+ self.doubleStreamLayers = doubleStreamLayers
28
+ self.singleStreamLayers = singleStreamLayers
29
+ self.layerCount = doubleStreamLayers.count + singleStreamLayers.count
30
+ }
31
+
32
+ public var arrays: [MLXArray] {
33
+ doubleStreamLayers.flatMap { [$0.k, $0.v] } +
34
+ singleStreamLayers.flatMap { [$0.k, $0.v] }
35
+ }
36
+ }
@@ -0,0 +1,193 @@
1
+ // Flux2Modulation.swift - AdaLN Modulation for Flux.2
2
+ // Copyright 2025 Vincent Gourbin
3
+
4
+ import Foundation
5
+ import MLX
6
+ import MLXNN
7
+
8
+ /// Modulation parameters for a single AdaLN operation
9
+ public struct ModulationParams {
10
+ /// Shift parameter for centering
11
+ public let shift: MLXArray
12
+ /// Scale parameter for variance
13
+ public let scale: MLXArray
14
+ /// Gate parameter for residual connection
15
+ public let gate: MLXArray
16
+
17
+ public init(shift: MLXArray, scale: MLXArray, gate: MLXArray) {
18
+ self.shift = shift
19
+ self.scale = scale
20
+ self.gate = gate
21
+ }
22
+ }
23
+
24
+ /// Adaptive Layer Normalization Modulation
25
+ ///
26
+ /// Generates (shift, scale, gate) parameters from timestep embeddings
27
+ /// for adaptive layer normalization in transformer blocks.
28
+ public class Flux2Modulation: Module, @unchecked Sendable {
29
+ let dim: Int
30
+ let numSets: Int
31
+ @ModuleInfo var linear: Linear
32
+
33
+ /// Initialize modulation layer
34
+ /// - Parameters:
35
+ /// - dim: Model dimension (6144 for Flux.2)
36
+ /// - numSets: Number of modulation parameter sets (e.g., 6 for double-stream, 3 for single)
37
+ public init(dim: Int, numSets: Int) {
38
+ self.dim = dim
39
+ self.numSets = numSets
40
+ // Each set has 3 parameters: shift, scale, gate
41
+ // So output dim = dim * 3 * numSets
42
+ // IMPORTANT: bias=false to match diffusers checkpoint
43
+ self.linear = Linear(dim, dim * 3 * numSets, bias: false)
44
+ }
45
+
46
+ /// Generate modulation parameters
47
+ /// - Parameter embedding: Timestep/conditioning embedding [B, dim]
48
+ /// - Returns: Array of ModulationParams, one for each set
49
+ public func callAsFunction(_ embedding: MLXArray) -> [ModulationParams] {
50
+ Flux2Debug.verbose("Modulation input shape: \(embedding.shape)")
51
+
52
+ // Apply SiLU activation before projection (common in DiT-style models)
53
+ let activated = silu(embedding)
54
+ Flux2Debug.verbose("After silu: \(activated.shape)")
55
+
56
+ // Project to all modulation parameters
57
+ let allParams = linear(activated) // [B, dim * 3 * numSets]
58
+ Flux2Debug.verbose("After linear (dim=\(dim), numSets=\(numSets)): \(allParams.shape)")
59
+
60
+ let batchSize = allParams.shape[0]
61
+ let reshapedParams = allParams.reshaped([batchSize, numSets, 3, dim])
62
+ Flux2Debug.verbose("After reshape: \(reshapedParams.shape)")
63
+
64
+ // Split into individual parameter sets
65
+ var result: [ModulationParams] = []
66
+
67
+ for i in 0..<numSets {
68
+ let shift = reshapedParams[0..., i, 0, 0...]
69
+ let scale = reshapedParams[0..., i, 1, 0...]
70
+ let gate = reshapedParams[0..., i, 2, 0...]
71
+
72
+ Flux2Debug.verbose("Set \(i) - shift: \(shift.shape), scale: \(scale.shape), gate: \(gate.shape)")
73
+
74
+ result.append(ModulationParams(shift: shift, scale: scale, gate: gate))
75
+ }
76
+
77
+ return result
78
+ }
79
+
80
+ /// Generate modulation parameters as a flat array for efficient processing
81
+ /// - Parameter embedding: Timestep/conditioning embedding [B, dim]
82
+ /// - Returns: Tensor [B, numSets, 3, dim] containing all shift/scale/gate
83
+ public func forwardFlat(_ embedding: MLXArray) -> MLXArray {
84
+ let activated = silu(embedding)
85
+ let allParams = linear(activated) // [B, dim * 3 * numSets]
86
+
87
+ // Reshape to [B, numSets, 3, dim]
88
+ let batchSize = allParams.shape[0]
89
+ return allParams.reshaped([batchSize, numSets, 3, dim])
90
+ }
91
+ }
92
+
93
+ /// Apply AdaLN modulation to normalized input
94
+ /// - Parameters:
95
+ /// - x: Normalized input [B, S, dim]
96
+ /// - shift: Shift parameter [B, dim]
97
+ /// - scale: Scale parameter [B, dim]
98
+ /// - Returns: Modulated output [B, S, dim]
99
+ public func applyModulation(
100
+ _ x: MLXArray,
101
+ shift: MLXArray,
102
+ scale: MLXArray
103
+ ) -> MLXArray {
104
+ Flux2Debug.verbose("applyModulation - x: \(x.shape), shift: \(shift.shape), scale: \(scale.shape)")
105
+
106
+ // Expand shift/scale for sequence dimension
107
+ // x: [B, S, dim], shift/scale: [B, dim]
108
+ let shiftExpanded = shift.expandedDimensions(axis: 1) // [B, 1, dim]
109
+ let scaleExpanded = scale.expandedDimensions(axis: 1) // [B, 1, dim]
110
+
111
+ Flux2Debug.verbose("Expanded - shift: \(shiftExpanded.shape), scale: \(scaleExpanded.shape)")
112
+
113
+ // Apply: x * (1 + scale) + shift
114
+ return x * (1 + scaleExpanded) + shiftExpanded
115
+ }
116
+
117
+ /// Apply gating to residual connection
118
+ /// - Parameters:
119
+ /// - residual: Residual to be gated [B, S, dim]
120
+ /// - gate: Gate parameter [B, dim]
121
+ /// - Returns: Gated residual [B, S, dim]
122
+ public func applyGate(_ residual: MLXArray, gate: MLXArray) -> MLXArray {
123
+ let gateExpanded = gate.expandedDimensions(axis: 1) // [B, 1, dim]
124
+ return residual * gateExpanded
125
+ }
126
+
127
+ /// Adaptive Layer Norm with continuous embedding (for final output)
128
+ public class AdaLayerNormContinuous: Module, @unchecked Sendable {
129
+ let dim: Int
130
+ let norm: LayerNorm
131
+ @ModuleInfo var linear: Linear
132
+
133
+ public init(dim: Int, eps: Float = 1e-6, usesBias: Bool = false) {
134
+ self.dim = dim
135
+ self.norm = LayerNorm(dimensions: dim, eps: eps, affine: false)
136
+ self._linear = ModuleInfo(wrappedValue: Linear(dim, dim * 2, bias: usesBias))
137
+ }
138
+
139
+ /// Apply adaptive layer norm
140
+ /// - Parameters:
141
+ /// - x: Input tensor [B, S, dim]
142
+ /// - conditioning: Conditioning embedding [B, dim]
143
+ /// - Returns: Normalized and modulated output [B, S, dim]
144
+ public func callAsFunction(_ x: MLXArray, conditioning: MLXArray) -> MLXArray {
145
+ // Get scale and shift from conditioning
146
+ // IMPORTANT: diffusers order is linear(silu(x)), NOT silu(linear(x))
147
+ // And the split order is (scale, shift), NOT (shift, scale)
148
+ let params = linear(silu(conditioning)) // [B, dim * 2]
149
+ let scale = params[0..., 0..<dim]
150
+ let shift = params[0..., dim...]
151
+
152
+ // Normalize
153
+ let normalized = norm(x)
154
+
155
+ // Apply modulation
156
+ return applyModulation(normalized, shift: shift, scale: scale)
157
+ }
158
+ }
159
+
160
+ /// Modulation for double-stream blocks
161
+ /// Generates 6 sets of parameters: 2 for image attention, 2 for image FFN, 2 for text
162
+ public class DoubleStreamModulation: Module, @unchecked Sendable {
163
+ let imgModulation: Flux2Modulation
164
+ let txtModulation: Flux2Modulation
165
+
166
+ public init(dim: Int) {
167
+ // Image branch: attn (shift, scale, gate) + ffn (shift, scale, gate) = 2 sets * 3
168
+ self.imgModulation = Flux2Modulation(dim: dim, numSets: 2)
169
+ // Text branch: attn (shift, scale, gate) + ffn (shift, scale, gate) = 2 sets * 3
170
+ self.txtModulation = Flux2Modulation(dim: dim, numSets: 2)
171
+ }
172
+
173
+ public func callAsFunction(_ embedding: MLXArray) -> (img: [ModulationParams], txt: [ModulationParams]) {
174
+ let imgParams = imgModulation(embedding)
175
+ let txtParams = txtModulation(embedding)
176
+ return (img: imgParams, txt: txtParams)
177
+ }
178
+ }
179
+
180
+ /// Modulation for single-stream blocks
181
+ /// Generates 2 sets: attention and FFN (fused in parallel attention)
182
+ public class SingleStreamModulation: Module, @unchecked Sendable {
183
+ let modulation: Flux2Modulation
184
+
185
+ public init(dim: Int) {
186
+ // Single stream: combined attn+ffn modulation = 1 set * 3
187
+ self.modulation = Flux2Modulation(dim: dim, numSets: 1)
188
+ }
189
+
190
+ public func callAsFunction(_ embedding: MLXArray) -> [ModulationParams] {
191
+ modulation(embedding)
192
+ }
193
+ }