bleam 0.0.10 → 0.0.12

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 (90) hide show
  1. package/dist/ai.cjs +1872 -1182
  2. package/dist/ai.d.cts +267 -234
  3. package/dist/ai.d.ts +267 -234
  4. package/dist/ai.js +1859 -1167
  5. package/dist/cli.cjs +3 -183
  6. package/dist/cli.d.cts +3 -47
  7. package/dist/cli.d.ts +3 -47
  8. package/dist/cli.js +3 -183
  9. package/dist/config.d.cts +1 -1
  10. package/dist/config.d.ts +1 -1
  11. package/dist/{files-Ds1wT8C2.js → files-BXVkPrPN.js} +6 -1
  12. package/dist/{files-Bo7h9fik.cjs → files-DxaQ-Nv0.cjs} +11 -0
  13. package/dist/files.cjs +1 -1
  14. package/dist/files.js +1 -1
  15. package/dist/index.d.cts +1 -1
  16. package/dist/index.d.ts +1 -1
  17. package/dist/platform.cjs +1 -13
  18. package/dist/platform.d.cts +1 -28
  19. package/dist/platform.d.ts +1 -28
  20. package/dist/platform.js +1 -10
  21. package/dist/schema-B5BfdswF.js +226 -0
  22. package/dist/schema-BnVZOXfu.cjs +286 -0
  23. package/dist/schema-D5eImHxu.d.cts +125 -0
  24. package/dist/schema-SSjokbtw.d.ts +125 -0
  25. package/dist/schema.cjs +10 -2
  26. package/dist/schema.d.cts +2 -2
  27. package/dist/schema.d.ts +2 -2
  28. package/dist/schema.js +2 -2
  29. package/dist/state-Dh3HLixb.js +874 -0
  30. package/dist/state-LssDgpff.cjs +973 -0
  31. package/dist/state.cjs +16 -12
  32. package/dist/state.d.cts +144 -140
  33. package/dist/state.d.ts +145 -140
  34. package/dist/state.js +3 -3
  35. package/dist/{ui-Bg11tvlc.d.ts → ui-1WepaMS4.d.cts} +1 -1
  36. package/dist/{ui-Dd7SXdbg.d.cts → ui-D7bRLYee.d.ts} +7 -7
  37. package/dist/ui.d.cts +1 -1
  38. package/dist/ui.d.ts +1 -1
  39. package/dist/window.d.cts +1 -1
  40. package/dist/window.d.ts +1 -1
  41. package/package.json +3 -1
  42. package/templates/foundation-models/app/index.tsx +77 -15
  43. package/templates/image-generation/app/index.tsx +4 -2
  44. package/templates/native/ios/Bleam/AI/Flux2/Configuration/ModelRegistry.swift +1 -1
  45. package/templates/native/ios/Bleam/AI/Flux2/Pipeline/FluxImageRunner.swift +7 -2
  46. package/templates/native/ios/Bleam/AI/TextGeneration/Bonsai/BonsaiTextGenerator.swift +46 -0
  47. package/templates/native/ios/Bleam/AI/TextGeneration/Bonsai/BonsaiTextModel.swift +107 -0
  48. package/templates/native/ios/Bleam/AI/TextGeneration/Bonsai/BonsaiTextSnapshotValidator.swift +201 -0
  49. package/templates/native/ios/Bleam/AI/TextGeneration/Core/GenerationTypes.swift +85 -0
  50. package/templates/native/ios/Bleam/AI/TextGeneration/Core/ModelCapabilities.swift +21 -0
  51. package/templates/native/ios/Bleam/AI/TextGeneration/Core/TextKVCache.swift +23 -0
  52. package/templates/native/ios/Bleam/AI/TextGeneration/Core/TokenSampler.swift +79 -0
  53. package/templates/native/ios/Bleam/AI/TextGeneration/Qwen3/Qwen3CheckpointLoader.swift +161 -0
  54. package/templates/native/ios/Bleam/AI/TextGeneration/Qwen3/Qwen3Configuration.swift +119 -0
  55. package/templates/native/ios/Bleam/AI/TextGeneration/Qwen3/Qwen3InferenceSession.swift +84 -0
  56. package/templates/native/ios/Bleam/AI/TextGeneration/Qwen3/Qwen3Layers.swift +197 -0
  57. package/templates/native/ios/Bleam/AI/TextGeneration/Qwen3/Qwen3Model.swift +130 -0
  58. package/templates/native/ios/Bleam/AI/TextGeneration/Qwen35/Qwen35Checkpoint.swift +102 -0
  59. package/templates/native/ios/Bleam/AI/TextGeneration/Qwen35/Qwen35Configuration.swift +140 -0
  60. package/templates/native/ios/Bleam/AI/TextGeneration/Qwen35/Qwen35Model.swift +112 -0
  61. package/templates/native/ios/Bleam/AI/TextGeneration/Qwen35/Qwen35Session.swift +121 -0
  62. package/templates/native/ios/Bleam/AI/TextGeneration/QwenHybrid/QwenHybridCache.swift +42 -0
  63. package/templates/native/ios/Bleam/AI/TextGeneration/QwenHybrid/QwenHybridFullAttention.swift +57 -0
  64. package/templates/native/ios/Bleam/AI/TextGeneration/QwenHybrid/QwenHybridLayers.swift +58 -0
  65. package/templates/native/ios/Bleam/AI/TextGeneration/QwenHybrid/QwenHybridLinearAttention.swift +111 -0
  66. package/templates/native/ios/Bleam/AppDelegate.swift +0 -206
  67. package/templates/native/ios/Bleam.xcodeproj/project.pbxproj +135 -129
  68. package/templates/native/ios/GenerationService/BonsaiTextGenerationRunner.swift +103 -0
  69. package/templates/native/ios/GenerationService/ImageGenerationRunner.swift +19 -7
  70. package/templates/native/ios/GenerationService/main.swift +174 -48
  71. package/templates/native/ios/Podfile.lock +173 -173
  72. package/templates/native/ios/Shared/Generation/GenerationServiceProtocol.swift +3 -0
  73. package/templates/native/ios/Shared/Generation/GenerationWorkerProtocol.swift +44 -0
  74. package/templates/native/modules/bleam-runtime/ios/AIModule.swift +239 -341
  75. package/templates/native/modules/bleam-runtime/ios/GenerationContracts.swift +47 -0
  76. package/templates/native/modules/bleam-runtime/ios/PlatformModule.swift +0 -191
  77. package/templates/text-generation/app/index.tsx +124 -0
  78. package/templates/text-generation/app.config.ts +5 -0
  79. package/templates/updates/README.md +43 -199
  80. package/templates/updates/src/index.ts +5 -98
  81. package/templates/updates/src/schema.ts +18 -173
  82. package/dist/schema-Bo5Jvqus.js +0 -90
  83. package/dist/schema-DOOjfXvs.d.ts +0 -58
  84. package/dist/schema-ENSMX_1t.d.cts +0 -58
  85. package/dist/schema-rQ13mrpD.cjs +0 -102
  86. package/dist/state-Bx0VlTlO.cjs +0 -852
  87. package/dist/state-CAwe-Vw1.js +0 -767
  88. package/templates/native/ios/PlatformHelper/main.swift +0 -726
  89. /package/dist/{config-Cms0rvqg.d.ts → config-COcRnn5a.d.cts} +0 -0
  90. /package/dist/{config-CufOVJV3.d.cts → config-Chi-flpJ.d.ts} +0 -0
@@ -2,68 +2,130 @@ import Foundation
2
2
  import MLX
3
3
 
4
4
  private final class GenerationRuntime: @unchecked Sendable {
5
+ private enum OperationKind {
6
+ case image
7
+ case text
8
+ }
9
+
5
10
  private let modelWarmTimeout: Duration = .seconds(60)
11
+ private let stateLock = NSLock()
12
+ private var activeID: String?
13
+ private var activeKind: OperationKind?
6
14
  private var activeTask: Task<Void, Never>?
7
- private var activeRunner: ImageGenerationRunner?
15
+ private var activeImageRunner: ImageGenerationRunner?
16
+ private var activeTextRunner: BonsaiTextGenerationRunner?
8
17
  private var trimTask: Task<Void, Never>?
9
18
  private var isCancelled = false
19
+ private var operationEpoch: UInt64 = 0
20
+ private var isShuttingDown = false
10
21
 
11
22
  var isRunning: Bool {
12
- activeTask != nil
23
+ stateLock.withLock { activeTask != nil }
13
24
  }
14
25
 
15
- func start(
26
+ func startImage(
16
27
  request: GenerationWorkerRequest,
17
28
  emit: @escaping @Sendable (GenerationWorkerEvent) -> Void
18
29
  ) -> Bool {
19
- guard activeTask == nil else { return false }
30
+ stateLock.withLock {
31
+ guard activeTask == nil, !isShuttingDown else { return false }
32
+ trimTask?.cancel()
33
+ trimTask = nil
34
+ isCancelled = false
35
+ operationEpoch &+= 1
36
+ activeID = request.jobID.uuidString
37
+ activeKind = .image
38
+ let runner = ImageGenerationRunner(request: request, emit: emit)
39
+ activeImageRunner = runner
40
+ activeTask = Task { [weak self] in
41
+ await self?.runImage(request, runner: runner, emit: emit)
42
+ }
43
+ return true
44
+ }
45
+ }
46
+
47
+ func startText(
48
+ request: BonsaiTextGenerationRequest,
49
+ emit: @escaping @Sendable (BonsaiTextGenerationEvent) -> Void
50
+ ) -> Bool {
51
+ stateLock.withLock {
52
+ guard activeTask == nil, !isShuttingDown else { return false }
53
+ trimTask?.cancel()
54
+ trimTask = nil
55
+ isCancelled = false
56
+ operationEpoch &+= 1
57
+ activeID = request.id
58
+ activeKind = .text
59
+ let runner = BonsaiTextGenerationRunner(request: request, emit: emit)
60
+ activeTextRunner = runner
61
+ activeTask = Task { [weak self] in
62
+ await self?.runText(request, runner: runner)
63
+ }
64
+ return true
65
+ }
66
+ }
67
+
68
+ func cancelImage() -> Bool {
69
+ let operation = stateLock.withLock { () -> (Task<Void, Never>?, ImageGenerationRunner?)? in
70
+ guard activeKind == .image else { return nil }
71
+ isCancelled = true
72
+ trimTask?.cancel()
73
+ trimTask = nil
74
+ return (activeTask, activeImageRunner)
75
+ }
76
+ guard let operation else { return false }
77
+ operation.0?.cancel()
78
+ operation.1?.cancel()
79
+ return true
80
+ }
20
81
 
21
- trimTask?.cancel()
22
- trimTask = nil
23
- isCancelled = false
24
- activeTask = Task { [weak self] in
25
- await self?.run(request, emit: emit)
82
+ func cancelText(id: String) -> Bool {
83
+ let operation = stateLock.withLock { () -> (Task<Void, Never>?, BonsaiTextGenerationRunner?)? in
84
+ guard activeKind == .text, activeID == id else { return nil }
85
+ isCancelled = true
86
+ trimTask?.cancel()
87
+ trimTask = nil
88
+ return (activeTask, activeTextRunner)
26
89
  }
90
+ guard let operation else { return false }
91
+ operation.0?.cancel()
92
+ operation.1?.cancel()
27
93
  return true
28
94
  }
29
95
 
30
- func cancel() {
31
- isCancelled = true
32
- trimTask?.cancel()
33
- trimTask = nil
34
- activeTask?.cancel()
35
- activeRunner?.cancel()
96
+ func beginShutdown() -> Bool {
97
+ stateLock.withLock {
98
+ guard activeTask == nil, !isShuttingDown else { return false }
99
+ isShuttingDown = true
100
+ operationEpoch &+= 1
101
+ trimTask?.cancel()
102
+ trimTask = nil
103
+ return true
104
+ }
36
105
  }
37
106
 
38
107
  func trimModelsNow() async {
39
- trimTask?.cancel()
40
- trimTask = nil
41
- // We currently reload every stage per generation, but keep the explicit
42
- // trim hook from ImageStudio's service architecture. Warm model caching
43
- // should be added here later, never back in the app process.
44
- MLX.Memory.clearCache()
45
- await Task.yield()
46
- MLX.Memory.clearCache()
108
+ stateLock.withLock {
109
+ trimTask?.cancel()
110
+ trimTask = nil
111
+ MLX.Memory.clearCache()
112
+ MLX.Memory.clearCache()
113
+ }
47
114
  print("[GenerationService] trim active=\(MLX.Memory.activeMemory / 1_048_576)MB cache=\(MLX.Memory.cacheMemory / 1_048_576)MB")
48
115
  }
49
116
 
50
- private func run(
117
+ private func runImage(
51
118
  _ request: GenerationWorkerRequest,
119
+ runner: ImageGenerationRunner,
52
120
  emit: @escaping @Sendable (GenerationWorkerEvent) -> Void
53
121
  ) async {
54
- let runner = ImageGenerationRunner(request: request, emit: emit)
55
- activeRunner = runner
56
122
  defer {
57
- if activeRunner === runner {
58
- activeRunner = nil
59
- }
60
- activeTask = nil
61
- scheduleTrimAfterIdle()
123
+ finish(id: request.jobID.uuidString, imageRunner: runner)
62
124
  }
63
125
 
64
126
  do {
65
127
  try await runner.run(isCancelled: { [weak self] in
66
- Task.isCancelled || (self?.isCancelled ?? false)
128
+ Task.isCancelled || (self?.cancellationRequested ?? false)
67
129
  })
68
130
  } catch is CancellationError {
69
131
  emit(.init(type: .canceled))
@@ -76,17 +138,59 @@ private final class GenerationRuntime: @unchecked Sendable {
76
138
  }
77
139
  }
78
140
 
141
+ private func runText(
142
+ _ request: BonsaiTextGenerationRequest,
143
+ runner: BonsaiTextGenerationRunner
144
+ ) async {
145
+ defer { finish(id: request.id, textRunner: runner) }
146
+ await runner.run(isCancelled: { [weak self] in
147
+ Task.isCancelled || (self?.cancellationRequested ?? true)
148
+ })
149
+ }
150
+
151
+ private var cancellationRequested: Bool {
152
+ stateLock.withLock { isCancelled }
153
+ }
154
+
155
+ private func finish(
156
+ id: String,
157
+ imageRunner: ImageGenerationRunner? = nil,
158
+ textRunner: BonsaiTextGenerationRunner? = nil
159
+ ) {
160
+ let finished = stateLock.withLock {
161
+ guard activeID == id else { return false }
162
+ if let imageRunner, activeImageRunner !== imageRunner { return false }
163
+ if let textRunner, activeTextRunner !== textRunner { return false }
164
+ activeID = nil
165
+ activeKind = nil
166
+ activeImageRunner = nil
167
+ activeTextRunner = nil
168
+ activeTask = nil
169
+ isCancelled = false
170
+ return true
171
+ }
172
+ if finished { scheduleTrimAfterIdle() }
173
+ }
174
+
79
175
  private func scheduleTrimAfterIdle() {
80
- trimTask?.cancel()
81
- trimTask = Task { [weak self] in
82
- guard let self else { return }
83
- do {
84
- try await Task.sleep(for: modelWarmTimeout)
85
- } catch {
86
- return
176
+ stateLock.withLock {
177
+ trimTask?.cancel()
178
+ let scheduledEpoch = operationEpoch
179
+ trimTask = Task { [weak self] in
180
+ guard let self else { return }
181
+ do {
182
+ try await Task.sleep(for: modelWarmTimeout)
183
+ } catch {
184
+ return
185
+ }
186
+ guard !Task.isCancelled else { return }
187
+ self.stateLock.withLock {
188
+ guard self.activeTask == nil, self.operationEpoch == scheduledEpoch else { return }
189
+ self.trimTask = nil
190
+ MLX.Memory.clearCache()
191
+ MLX.Memory.clearCache()
192
+ }
87
193
  }
88
- guard !Task.isCancelled else { return }
89
- await self.trimModelsNow()
90
194
  }
91
195
  print("[GenerationService] model trim scheduled 60s")
92
196
  }
@@ -94,10 +198,11 @@ private final class GenerationRuntime: @unchecked Sendable {
94
198
 
95
199
  private final class GenerationService: NSObject, GenerationServiceProtocol {
96
200
  private weak var connection: NSXPCConnection?
97
- private let runtime = GenerationRuntime()
201
+ private let runtime: GenerationRuntime
98
202
 
99
- init(connection: NSXPCConnection) {
203
+ init(connection: NSXPCConnection, runtime: GenerationRuntime) {
100
204
  self.connection = connection
205
+ self.runtime = runtime
101
206
  }
102
207
 
103
208
  func startGeneration(requestData: Data, reply: @escaping (Bool) -> Void) {
@@ -107,14 +212,27 @@ private final class GenerationService: NSObject, GenerationServiceProtocol {
107
212
  return
108
213
  }
109
214
 
110
- reply(runtime.start(request: request) { [weak self] event in
215
+ reply(runtime.startImage(request: request) { [weak self] event in
111
216
  self?.emit(event)
112
217
  })
113
218
  }
114
219
 
115
220
  func cancelGeneration(reply: @escaping (Bool) -> Void) {
116
- runtime.cancel()
117
- reply(true)
221
+ reply(runtime.cancelImage())
222
+ }
223
+
224
+ func startTextGeneration(requestData: Data, reply: @escaping (Bool) -> Void) {
225
+ guard let request = try? JSONDecoder().decode(BonsaiTextGenerationRequest.self, from: requestData) else {
226
+ reply(false)
227
+ return
228
+ }
229
+ reply(runtime.startText(request: request) { [weak self] event in
230
+ self?.emitText(event)
231
+ })
232
+ }
233
+
234
+ func cancelTextGeneration(id: String, reply: @escaping (Bool) -> Void) {
235
+ reply(runtime.cancelText(id: id))
118
236
  }
119
237
 
120
238
  func memoryTelemetry(reply: @escaping (Data?) -> Void) {
@@ -126,7 +244,7 @@ private final class GenerationService: NSObject, GenerationServiceProtocol {
126
244
  }
127
245
 
128
246
  func shutdown(reply: @escaping (Bool) -> Void) {
129
- guard !runtime.isRunning else {
247
+ guard runtime.beginShutdown() else {
130
248
  reply(false)
131
249
  return
132
250
  }
@@ -142,13 +260,21 @@ private final class GenerationService: NSObject, GenerationServiceProtocol {
142
260
  let client = connection?.remoteObjectProxy as? GenerationServiceClientProtocol
143
261
  client?.generationWorkerDidEmit(eventData: data)
144
262
  }
263
+
264
+ private func emitText(_ event: BonsaiTextGenerationEvent) {
265
+ guard let data = try? JSONEncoder().encode(event) else { return }
266
+ let client = connection?.remoteObjectProxy as? GenerationServiceClientProtocol
267
+ client?.textGenerationWorkerDidEmit(eventData: data)
268
+ }
145
269
  }
146
270
 
147
271
  private final class GenerationServiceDelegate: NSObject, NSXPCListenerDelegate {
272
+ private let runtime = GenerationRuntime()
273
+
148
274
  func listener(_ listener: NSXPCListener, shouldAcceptNewConnection connection: NSXPCConnection) -> Bool {
149
275
  connection.exportedInterface = NSXPCInterface(with: GenerationServiceProtocol.self)
150
276
  connection.remoteObjectInterface = NSXPCInterface(with: GenerationServiceClientProtocol.self)
151
- connection.exportedObject = GenerationService(connection: connection)
277
+ connection.exportedObject = GenerationService(connection: connection, runtime: runtime)
152
278
  connection.resume()
153
279
  return true
154
280
  }