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.
- package/dist/ai.cjs +1872 -1182
- package/dist/ai.d.cts +267 -234
- package/dist/ai.d.ts +267 -234
- package/dist/ai.js +1859 -1167
- package/dist/cli.cjs +3 -183
- package/dist/cli.d.cts +3 -47
- package/dist/cli.d.ts +3 -47
- package/dist/cli.js +3 -183
- package/dist/config.d.cts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/{files-Ds1wT8C2.js → files-BXVkPrPN.js} +6 -1
- package/dist/{files-Bo7h9fik.cjs → files-DxaQ-Nv0.cjs} +11 -0
- package/dist/files.cjs +1 -1
- package/dist/files.js +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/platform.cjs +1 -13
- package/dist/platform.d.cts +1 -28
- package/dist/platform.d.ts +1 -28
- package/dist/platform.js +1 -10
- package/dist/schema-B5BfdswF.js +226 -0
- package/dist/schema-BnVZOXfu.cjs +286 -0
- package/dist/schema-D5eImHxu.d.cts +125 -0
- package/dist/schema-SSjokbtw.d.ts +125 -0
- package/dist/schema.cjs +10 -2
- package/dist/schema.d.cts +2 -2
- package/dist/schema.d.ts +2 -2
- package/dist/schema.js +2 -2
- package/dist/state-Dh3HLixb.js +874 -0
- package/dist/state-LssDgpff.cjs +973 -0
- package/dist/state.cjs +16 -12
- package/dist/state.d.cts +144 -140
- package/dist/state.d.ts +145 -140
- package/dist/state.js +3 -3
- package/dist/{ui-Bg11tvlc.d.ts → ui-1WepaMS4.d.cts} +1 -1
- package/dist/{ui-Dd7SXdbg.d.cts → ui-D7bRLYee.d.ts} +7 -7
- package/dist/ui.d.cts +1 -1
- package/dist/ui.d.ts +1 -1
- package/dist/window.d.cts +1 -1
- package/dist/window.d.ts +1 -1
- package/package.json +3 -1
- package/templates/foundation-models/app/index.tsx +77 -15
- package/templates/image-generation/app/index.tsx +4 -2
- package/templates/native/ios/Bleam/AI/Flux2/Configuration/ModelRegistry.swift +1 -1
- package/templates/native/ios/Bleam/AI/Flux2/Pipeline/FluxImageRunner.swift +7 -2
- package/templates/native/ios/Bleam/AI/TextGeneration/Bonsai/BonsaiTextGenerator.swift +46 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Bonsai/BonsaiTextModel.swift +107 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Bonsai/BonsaiTextSnapshotValidator.swift +201 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Core/GenerationTypes.swift +85 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Core/ModelCapabilities.swift +21 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Core/TextKVCache.swift +23 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Core/TokenSampler.swift +79 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen3/Qwen3CheckpointLoader.swift +161 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen3/Qwen3Configuration.swift +119 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen3/Qwen3InferenceSession.swift +84 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen3/Qwen3Layers.swift +197 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen3/Qwen3Model.swift +130 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen35/Qwen35Checkpoint.swift +102 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen35/Qwen35Configuration.swift +140 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen35/Qwen35Model.swift +112 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/Qwen35/Qwen35Session.swift +121 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/QwenHybrid/QwenHybridCache.swift +42 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/QwenHybrid/QwenHybridFullAttention.swift +57 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/QwenHybrid/QwenHybridLayers.swift +58 -0
- package/templates/native/ios/Bleam/AI/TextGeneration/QwenHybrid/QwenHybridLinearAttention.swift +111 -0
- package/templates/native/ios/Bleam/AppDelegate.swift +0 -206
- package/templates/native/ios/Bleam.xcodeproj/project.pbxproj +135 -129
- package/templates/native/ios/GenerationService/BonsaiTextGenerationRunner.swift +103 -0
- package/templates/native/ios/GenerationService/ImageGenerationRunner.swift +19 -7
- package/templates/native/ios/GenerationService/main.swift +174 -48
- package/templates/native/ios/Podfile.lock +173 -173
- package/templates/native/ios/Shared/Generation/GenerationServiceProtocol.swift +3 -0
- package/templates/native/ios/Shared/Generation/GenerationWorkerProtocol.swift +44 -0
- package/templates/native/modules/bleam-runtime/ios/AIModule.swift +239 -341
- package/templates/native/modules/bleam-runtime/ios/GenerationContracts.swift +47 -0
- package/templates/native/modules/bleam-runtime/ios/PlatformModule.swift +0 -191
- package/templates/text-generation/app/index.tsx +124 -0
- package/templates/text-generation/app.config.ts +5 -0
- package/templates/updates/README.md +43 -199
- package/templates/updates/src/index.ts +5 -98
- package/templates/updates/src/schema.ts +18 -173
- package/dist/schema-Bo5Jvqus.js +0 -90
- package/dist/schema-DOOjfXvs.d.ts +0 -58
- package/dist/schema-ENSMX_1t.d.cts +0 -58
- package/dist/schema-rQ13mrpD.cjs +0 -102
- package/dist/state-Bx0VlTlO.cjs +0 -852
- package/dist/state-CAwe-Vw1.js +0 -767
- package/templates/native/ios/PlatformHelper/main.swift +0 -726
- /package/dist/{config-Cms0rvqg.d.ts → config-COcRnn5a.d.cts} +0 -0
- /package/dist/{config-CufOVJV3.d.cts → config-Chi-flpJ.d.ts} +0 -0
|
@@ -3,12 +3,15 @@ import Foundation
|
|
|
3
3
|
@objc protocol GenerationServiceProtocol {
|
|
4
4
|
func startGeneration(requestData: Data, reply: @escaping (Bool) -> Void)
|
|
5
5
|
func cancelGeneration(reply: @escaping (Bool) -> Void)
|
|
6
|
+
func startTextGeneration(requestData: Data, reply: @escaping (Bool) -> Void)
|
|
7
|
+
func cancelTextGeneration(id: String, reply: @escaping (Bool) -> Void)
|
|
6
8
|
func memoryTelemetry(reply: @escaping (Data?) -> Void)
|
|
7
9
|
func shutdown(reply: @escaping (Bool) -> Void)
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
@objc protocol GenerationServiceClientProtocol {
|
|
11
13
|
func generationWorkerDidEmit(eventData: Data)
|
|
14
|
+
func textGenerationWorkerDidEmit(eventData: Data)
|
|
12
15
|
}
|
|
13
16
|
|
|
14
17
|
struct GenerationWorkerRequest: Codable, Sendable {
|
|
@@ -133,3 +136,47 @@ struct GenerationWorkerLoRA: Codable, Sendable {
|
|
|
133
136
|
let path: String
|
|
134
137
|
let scale: Double
|
|
135
138
|
}
|
|
139
|
+
|
|
140
|
+
struct BonsaiTextGenerationRequest: Codable, Sendable {
|
|
141
|
+
let id: String
|
|
142
|
+
let model: String
|
|
143
|
+
let modelPath: String
|
|
144
|
+
let inputIDs: [Int]
|
|
145
|
+
let endTokenIDs: [Int]
|
|
146
|
+
let maximumNewTokens: Int?
|
|
147
|
+
let temperature: Double?
|
|
148
|
+
let topP: Double?
|
|
149
|
+
let topK: Int?
|
|
150
|
+
let repetitionPenalty: Double?
|
|
151
|
+
let repetitionContextSize: Int?
|
|
152
|
+
let seed: Int?
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
struct BonsaiTextGenerationEvent: Codable, Sendable {
|
|
156
|
+
enum EventType: String, Codable, Sendable {
|
|
157
|
+
case update
|
|
158
|
+
case completed
|
|
159
|
+
case canceled
|
|
160
|
+
case failed
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let type: EventType
|
|
164
|
+
let id: String
|
|
165
|
+
let tokenIDs: [Int]
|
|
166
|
+
let finishReason: String?
|
|
167
|
+
let message: String?
|
|
168
|
+
|
|
169
|
+
init(
|
|
170
|
+
type: EventType,
|
|
171
|
+
id: String,
|
|
172
|
+
tokenIDs: [Int] = [],
|
|
173
|
+
finishReason: String? = nil,
|
|
174
|
+
message: String? = nil
|
|
175
|
+
) {
|
|
176
|
+
self.type = type
|
|
177
|
+
self.id = id
|
|
178
|
+
self.tokenIDs = tokenIDs
|
|
179
|
+
self.finishReason = finishReason
|
|
180
|
+
self.message = message
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -6,70 +6,10 @@ import ExpoModulesCore
|
|
|
6
6
|
import Foundation
|
|
7
7
|
import Photos
|
|
8
8
|
|
|
9
|
-
@objc private protocol PlatformHelperProtocol {
|
|
10
|
-
func health(reply: @escaping (Data?) -> Void)
|
|
11
|
-
func downloadPlatformUpdate(requestData: Data, reply: @escaping (Data?) -> Void)
|
|
12
|
-
func applyPlatformUpdate(requestData: Data, reply: @escaping (Bool) -> Void)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
9
|
public final class PlatformModule: Module {
|
|
16
10
|
public func definition() -> ModuleDefinition {
|
|
17
11
|
Name("Platform")
|
|
18
12
|
|
|
19
|
-
AsyncFunction("helperStatus") { (promise: Promise) in
|
|
20
|
-
guard Self.isSupportedInstallLocation else {
|
|
21
|
-
UserDefaults.standard.set(false, forKey: "BleamPlatformUpdatesSupported")
|
|
22
|
-
promise.resolve([
|
|
23
|
-
"healthy": false,
|
|
24
|
-
"protocolVersion": 1,
|
|
25
|
-
"helperVersion": "unavailable",
|
|
26
|
-
"platformVersion": "unavailable",
|
|
27
|
-
"capabilities": [],
|
|
28
|
-
"updatesSupported": false,
|
|
29
|
-
"installPath": Bundle.main.bundleURL.path,
|
|
30
|
-
"reason": "reinstall_required",
|
|
31
|
-
])
|
|
32
|
-
return
|
|
33
|
-
}
|
|
34
|
-
let serviceName = "\(Bundle.main.bundleIdentifier ?? "dev.bleam.app").PlatformHelper"
|
|
35
|
-
guard let connection = Self.makeConnection(serviceName: serviceName) else {
|
|
36
|
-
promise.reject("HELPER_UNAVAILABLE", "The app helper is unavailable")
|
|
37
|
-
return
|
|
38
|
-
}
|
|
39
|
-
connection.remoteObjectInterface = NSXPCInterface(with: PlatformHelperProtocol.self)
|
|
40
|
-
connection.resume()
|
|
41
|
-
|
|
42
|
-
guard let service = connection.remoteObjectProxyWithErrorHandler({ error in
|
|
43
|
-
connection.invalidate()
|
|
44
|
-
promise.reject("HELPER_UNAVAILABLE", error.localizedDescription)
|
|
45
|
-
}) as? PlatformHelperProtocol else {
|
|
46
|
-
connection.invalidate()
|
|
47
|
-
promise.reject("HELPER_UNAVAILABLE", "The app helper is unavailable")
|
|
48
|
-
return
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
service.health { data in
|
|
52
|
-
defer { connection.invalidate() }
|
|
53
|
-
guard
|
|
54
|
-
let data,
|
|
55
|
-
var status = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
|
|
56
|
-
else {
|
|
57
|
-
promise.reject("HELPER_INVALID_RESPONSE", "The app helper returned an invalid response")
|
|
58
|
-
return
|
|
59
|
-
}
|
|
60
|
-
let supported = Self.isSupportedInstallLocation
|
|
61
|
-
status["updatesSupported"] = supported
|
|
62
|
-
status["installPath"] = Bundle.main.bundleURL.path
|
|
63
|
-
if !supported {
|
|
64
|
-
status["reason"] = "reinstall_required"
|
|
65
|
-
}
|
|
66
|
-
UserDefaults.standard.set(status["healthy"], forKey: "BleamHelperHealthy")
|
|
67
|
-
UserDefaults.standard.set(status["platformVersion"], forKey: "BleamHelperPlatformVersion")
|
|
68
|
-
UserDefaults.standard.set(supported, forKey: "BleamPlatformUpdatesSupported")
|
|
69
|
-
promise.resolve(status)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
13
|
AsyncFunction("permissionState") { (capability: String, promise: Promise) in
|
|
74
14
|
guard let state = Self.permissionState(capability) else {
|
|
75
15
|
promise.reject("INVALID_CAPABILITY", "Unknown permission capability: \(capability)")
|
|
@@ -85,132 +25,6 @@ public final class PlatformModule: Module {
|
|
|
85
25
|
}
|
|
86
26
|
promise.resolve(states)
|
|
87
27
|
}
|
|
88
|
-
|
|
89
|
-
AsyncFunction("downloadPlatformUpdate") { (promise: Promise) in
|
|
90
|
-
guard Self.isSupportedInstallLocation else {
|
|
91
|
-
promise.reject("REINSTALL_REQUIRED", "Platform updates require the app to be installed directly in ~/Applications")
|
|
92
|
-
return
|
|
93
|
-
}
|
|
94
|
-
guard
|
|
95
|
-
let serviceURL = Bundle.main.object(forInfoDictionaryKey: "BleamUpdateServiceURL") as? String,
|
|
96
|
-
let bundleIdentifier = Bundle.main.bundleIdentifier,
|
|
97
|
-
let teamIdentifier = Bundle.main.object(forInfoDictionaryKey: "BleamTeamIdentifier") as? String,
|
|
98
|
-
let platformVersion = Bundle.main.object(forInfoDictionaryKey: "BleamRuntimeVersion") as? String,
|
|
99
|
-
let platformFingerprint = Bundle.main.object(forInfoDictionaryKey: "BleamPlatformFingerprint") as? String,
|
|
100
|
-
let requestData = try? JSONSerialization.data(withJSONObject: [
|
|
101
|
-
"serviceURL": serviceURL,
|
|
102
|
-
"bundleIdentifier": bundleIdentifier,
|
|
103
|
-
"teamIdentifier": teamIdentifier,
|
|
104
|
-
"platformVersion": platformVersion,
|
|
105
|
-
"platformFingerprint": platformFingerprint,
|
|
106
|
-
])
|
|
107
|
-
else {
|
|
108
|
-
promise.reject("UPDATES_UNAVAILABLE", "Platform updates are not configured")
|
|
109
|
-
return
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
guard let connection = Self.makeConnection(serviceName: "\(bundleIdentifier).PlatformHelper") else {
|
|
113
|
-
promise.reject("HELPER_UNAVAILABLE", "The app helper is unavailable")
|
|
114
|
-
return
|
|
115
|
-
}
|
|
116
|
-
connection.remoteObjectInterface = NSXPCInterface(with: PlatformHelperProtocol.self)
|
|
117
|
-
connection.resume()
|
|
118
|
-
guard let service = connection.remoteObjectProxyWithErrorHandler({ error in
|
|
119
|
-
connection.invalidate()
|
|
120
|
-
promise.reject("PLATFORM_DOWNLOAD_FAILED", error.localizedDescription)
|
|
121
|
-
}) as? PlatformHelperProtocol else {
|
|
122
|
-
connection.invalidate()
|
|
123
|
-
promise.reject("HELPER_UNAVAILABLE", "The app helper is unavailable")
|
|
124
|
-
return
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
service.downloadPlatformUpdate(requestData: requestData) { data in
|
|
128
|
-
defer { connection.invalidate() }
|
|
129
|
-
guard
|
|
130
|
-
let data,
|
|
131
|
-
let result = try? JSONSerialization.jsonObject(with: data) as? [String: Any]
|
|
132
|
-
else {
|
|
133
|
-
promise.reject("PLATFORM_DOWNLOAD_FAILED", "The platform update could not be downloaded")
|
|
134
|
-
return
|
|
135
|
-
}
|
|
136
|
-
if let error = result["error"] as? String {
|
|
137
|
-
UserDefaults.standard.set(error, forKey: "BleamPlatformDownloadError")
|
|
138
|
-
promise.reject("PLATFORM_VERIFICATION_FAILED", error)
|
|
139
|
-
return
|
|
140
|
-
}
|
|
141
|
-
UserDefaults.standard.set(result["releaseId"], forKey: "BleamPlatformDownloadRelease")
|
|
142
|
-
promise.resolve(result)
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
AsyncFunction("updateAndRelaunch") { (releaseId: String, promise: Promise) in
|
|
147
|
-
guard Self.isSupportedInstallLocation else {
|
|
148
|
-
promise.reject("REINSTALL_REQUIRED", "Platform updates require the app to be installed directly in ~/Applications")
|
|
149
|
-
return
|
|
150
|
-
}
|
|
151
|
-
guard
|
|
152
|
-
let bundleIdentifier = Bundle.main.bundleIdentifier,
|
|
153
|
-
let teamIdentifier = Bundle.main.object(forInfoDictionaryKey: "BleamTeamIdentifier") as? String,
|
|
154
|
-
let requestData = try? JSONSerialization.data(withJSONObject: [
|
|
155
|
-
"releaseId": releaseId,
|
|
156
|
-
"bundleIdentifier": bundleIdentifier,
|
|
157
|
-
"teamIdentifier": teamIdentifier,
|
|
158
|
-
"installedAppPath": Bundle.main.bundleURL.path,
|
|
159
|
-
"appPID": ProcessInfo.processInfo.processIdentifier,
|
|
160
|
-
]),
|
|
161
|
-
let connection = Self.makeConnection(serviceName: "\(bundleIdentifier).PlatformHelper")
|
|
162
|
-
else {
|
|
163
|
-
promise.reject("PLATFORM_UPDATE_UNAVAILABLE", "The platform update cannot be applied")
|
|
164
|
-
return
|
|
165
|
-
}
|
|
166
|
-
connection.remoteObjectInterface = NSXPCInterface(with: PlatformHelperProtocol.self)
|
|
167
|
-
connection.resume()
|
|
168
|
-
guard let service = connection.remoteObjectProxyWithErrorHandler({ error in
|
|
169
|
-
connection.invalidate()
|
|
170
|
-
promise.reject("PLATFORM_UPDATE_FAILED", error.localizedDescription)
|
|
171
|
-
}) as? PlatformHelperProtocol else {
|
|
172
|
-
connection.invalidate()
|
|
173
|
-
promise.reject("HELPER_UNAVAILABLE", "The app helper is unavailable")
|
|
174
|
-
return
|
|
175
|
-
}
|
|
176
|
-
service.applyPlatformUpdate(requestData: requestData) { accepted in
|
|
177
|
-
connection.invalidate()
|
|
178
|
-
UserDefaults.standard.set(accepted, forKey: "BleamPlatformApplyAccepted")
|
|
179
|
-
guard accepted else {
|
|
180
|
-
promise.reject("PLATFORM_UPDATE_REJECTED", "The platform update is not verified or the app is not installed in ~/Applications")
|
|
181
|
-
return
|
|
182
|
-
}
|
|
183
|
-
promise.resolve(nil)
|
|
184
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
|
|
185
|
-
exit(0)
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
private static func makeConnection(serviceName: String) -> NSXPCConnection? {
|
|
192
|
-
#if targetEnvironment(macCatalyst)
|
|
193
|
-
let connectionClass: AnyObject = NSXPCConnection.self as AnyObject
|
|
194
|
-
let allocated = connectionClass.perform(NSSelectorFromString("alloc"))?.takeUnretainedValue() as AnyObject?
|
|
195
|
-
let selector = NSSelectorFromString("initWithMachServiceName:options:")
|
|
196
|
-
guard let allocated, allocated.responds(to: selector) else { return nil }
|
|
197
|
-
|
|
198
|
-
typealias Initializer = @convention(c) (
|
|
199
|
-
AnyObject,
|
|
200
|
-
Selector,
|
|
201
|
-
NSString,
|
|
202
|
-
UInt
|
|
203
|
-
) -> Unmanaged<AnyObject>
|
|
204
|
-
let initialize = unsafeBitCast(allocated.method(for: selector), to: Initializer.self)
|
|
205
|
-
return initialize(
|
|
206
|
-
allocated,
|
|
207
|
-
selector,
|
|
208
|
-
serviceName as NSString,
|
|
209
|
-
0
|
|
210
|
-
).takeRetainedValue() as? NSXPCConnection
|
|
211
|
-
#else
|
|
212
|
-
return NSXPCConnection(machServiceName: serviceName)
|
|
213
|
-
#endif
|
|
214
28
|
}
|
|
215
29
|
|
|
216
30
|
private static let permissionCapabilities = [
|
|
@@ -321,9 +135,4 @@ public final class PlatformModule: Module {
|
|
|
321
135
|
}
|
|
322
136
|
}
|
|
323
137
|
|
|
324
|
-
private static var isSupportedInstallLocation: Bool {
|
|
325
|
-
let applications = URL(fileURLWithPath: NSHomeDirectory(), isDirectory: true)
|
|
326
|
-
.appendingPathComponent("Applications", isDirectory: true).standardizedFileURL
|
|
327
|
-
return Bundle.main.bundleURL.standardizedFileURL.deletingLastPathComponent() == applications
|
|
328
|
-
}
|
|
329
138
|
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { bonsai, chat, prepareTextModel, useChat, useChatList } from 'bleam/ai'
|
|
2
|
+
import { sx } from 'bleam/styles'
|
|
3
|
+
import { Button } from 'bleam/ui'
|
|
4
|
+
import { Window } from 'bleam/window'
|
|
5
|
+
import { useEffect, useRef, useState } from 'react'
|
|
6
|
+
import { Text, TextInput, View } from 'react-native'
|
|
7
|
+
|
|
8
|
+
const appName = 'Bleam Basic'
|
|
9
|
+
const model = bonsai('ternary-bonsai-4b')
|
|
10
|
+
|
|
11
|
+
export default function App() {
|
|
12
|
+
const chats = useChatList()
|
|
13
|
+
const creating = useRef(false)
|
|
14
|
+
const [path, setPath] = useState<string | null>(null)
|
|
15
|
+
const [status, setStatus] = useState('Preparing local model...')
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
if (chats.length > 0 || creating.current) return
|
|
19
|
+
creating.current = true
|
|
20
|
+
void chat.create({ title: 'Private writing' }).catch((error) => {
|
|
21
|
+
setStatus(error instanceof Error ? error.message : String(error))
|
|
22
|
+
})
|
|
23
|
+
}, [chats.length])
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const controller = new AbortController()
|
|
27
|
+
void prepareTextModel(model, {
|
|
28
|
+
signal: controller.signal,
|
|
29
|
+
onProgress(progress) {
|
|
30
|
+
setStatus(
|
|
31
|
+
`Preparing model ${Math.round(progress.modelFraction * 100)}%`,
|
|
32
|
+
)
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
.then((prepared) => {
|
|
36
|
+
setPath(prepared.path)
|
|
37
|
+
setStatus('Ready')
|
|
38
|
+
})
|
|
39
|
+
.catch((error) => {
|
|
40
|
+
if (!controller.signal.aborted) {
|
|
41
|
+
setStatus(error instanceof Error ? error.message : String(error))
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
return () => controller.abort()
|
|
45
|
+
}, [])
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<View style={sx('flex-1', 'p-8', 'gap-6')}>
|
|
49
|
+
<Window title={appName} />
|
|
50
|
+
<Text style={sx('text-3xl', 'font-semibold')}>
|
|
51
|
+
Private Writing Assistant
|
|
52
|
+
</Text>
|
|
53
|
+
<Text style={sx('text-secondary-label')}>
|
|
54
|
+
Ternary Bonsai runs locally after its first model download. Chats return
|
|
55
|
+
after relaunch.
|
|
56
|
+
</Text>
|
|
57
|
+
<Text style={sx('text-secondary-label')}>{status}</Text>
|
|
58
|
+
{path && chats[0] ? <Conversation id={chats[0].id} path={path} /> : null}
|
|
59
|
+
</View>
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function Conversation({ id, path }: { id: string; path: string }) {
|
|
64
|
+
const conversation = useChat(id, {
|
|
65
|
+
model,
|
|
66
|
+
path,
|
|
67
|
+
system: 'You are a concise private writing assistant.',
|
|
68
|
+
})
|
|
69
|
+
const [prompt, setPrompt] = useState(
|
|
70
|
+
'Rewrite this note so it is clear and concise: We should probably meet sometime next week to discuss the launch.',
|
|
71
|
+
)
|
|
72
|
+
const latestAssistant = conversation.messages.findLast(
|
|
73
|
+
(message) => message.role === 'assistant',
|
|
74
|
+
)
|
|
75
|
+
const retryable =
|
|
76
|
+
latestAssistant?.status === 'failed' ||
|
|
77
|
+
latestAssistant?.status === 'canceled'
|
|
78
|
+
? latestAssistant
|
|
79
|
+
: null
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<View style={sx('gap-6')}>
|
|
83
|
+
<TextInput
|
|
84
|
+
multiline
|
|
85
|
+
value={prompt}
|
|
86
|
+
onChangeText={setPrompt}
|
|
87
|
+
style={sx(
|
|
88
|
+
{ minHeight: 128 },
|
|
89
|
+
'rounded-xl',
|
|
90
|
+
'border',
|
|
91
|
+
'border-separator',
|
|
92
|
+
'p-4',
|
|
93
|
+
)}
|
|
94
|
+
/>
|
|
95
|
+
<View style={sx('flex-row', 'gap-3')}>
|
|
96
|
+
<Button
|
|
97
|
+
title="Send"
|
|
98
|
+
loading={conversation.isGenerating}
|
|
99
|
+
disabled={!conversation.canSend}
|
|
100
|
+
onPress={() => void conversation.send(prompt)}
|
|
101
|
+
/>
|
|
102
|
+
<Button
|
|
103
|
+
title="Cancel"
|
|
104
|
+
variant="secondary"
|
|
105
|
+
disabled={!conversation.canCancel}
|
|
106
|
+
onPress={() => void conversation.cancel()}
|
|
107
|
+
/>
|
|
108
|
+
{retryable ? (
|
|
109
|
+
<Button
|
|
110
|
+
title="Retry"
|
|
111
|
+
variant="secondary"
|
|
112
|
+
onPress={() => void conversation.retry(retryable.id)}
|
|
113
|
+
/>
|
|
114
|
+
) : null}
|
|
115
|
+
</View>
|
|
116
|
+
{conversation.messages.map((message) => (
|
|
117
|
+
<Text key={message.id} style={sx('text-lg', 'text-label')}>
|
|
118
|
+
{message.role === 'user' ? 'You: ' : 'Assistant: '}
|
|
119
|
+
{message.content || message.status}
|
|
120
|
+
</Text>
|
|
121
|
+
))}
|
|
122
|
+
</View>
|
|
123
|
+
)
|
|
124
|
+
}
|