expo-ai-kit 0.2.0 → 0.2.1

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.
@@ -44,4 +44,5 @@ android {
44
44
 
45
45
  dependencies {
46
46
  implementation "com.google.mlkit:genai-prompt:1.0.0-alpha1"
47
+ implementation "com.google.mediapipe:tasks-genai:0.10.24"
47
48
  }
@@ -1,5 +1,7 @@
1
1
  package expo.modules.aikit
2
2
 
3
+ import android.app.ActivityManager
4
+ import android.content.Context
3
5
  import expo.modules.kotlin.modules.Module
4
6
  import expo.modules.kotlin.modules.ModuleDefinition
5
7
  import expo.modules.kotlin.functions.Coroutine
@@ -11,15 +13,28 @@ import kotlinx.coroutines.cancel
11
13
 
12
14
  class ExpoAiKitModule : Module() {
13
15
 
16
+ // Existing ML Kit client -- unchanged
14
17
  private val promptClient by lazy { PromptApiClient() }
18
+
19
+ // Gemma client -- lazy-initialized with app context
20
+ private val gemmaClient by lazy {
21
+ GemmaInferenceClient(appContext.reactContext ?: throw RuntimeException("React context not available"))
22
+ }
23
+
15
24
  private val activeStreamJobs = mutableMapOf<String, Job>()
16
25
  private val streamScope = CoroutineScope(Dispatchers.IO)
17
26
 
27
+ // Active model routing: "mlkit" (default) or a downloadable model ID
28
+ private var activeModelId: String = "mlkit"
29
+
18
30
  override fun definition() = ModuleDefinition {
19
31
  Name("ExpoAiKit")
20
32
 
21
- // Declare events that can be sent to JavaScript
22
- Events("onStreamToken")
33
+ Events("onStreamToken", "onDownloadProgress", "onModelStateChange")
34
+
35
+ // ==================================================================
36
+ // Existing inference API -- ML Kit path completely untouched
37
+ // ==================================================================
23
38
 
24
39
  Function("isAvailable") {
25
40
  promptClient.isAvailableBlocking()
@@ -42,7 +57,12 @@ class ExpoAiKitModule : Module() {
42
57
  "$role: $content"
43
58
  } + "\nASSISTANT:"
44
59
 
45
- val text = promptClient.generateText(conversationPrompt, systemPrompt)
60
+ // Route to active model
61
+ val text = if (activeModelId == "mlkit") {
62
+ promptClient.generateText(conversationPrompt, systemPrompt)
63
+ } else {
64
+ gemmaClient.generateText(conversationPrompt, systemPrompt)
65
+ }
46
66
  mapOf("text" to text)
47
67
  }
48
68
 
@@ -65,7 +85,7 @@ class ExpoAiKitModule : Module() {
65
85
 
66
86
  // Launch streaming in a coroutine that can be cancelled
67
87
  val job = streamScope.launch {
68
- promptClient.generateTextStream(conversationPrompt, systemPrompt) { token, accumulatedText, isDone ->
88
+ val streamCallback = { token: String, accumulatedText: String, isDone: Boolean ->
69
89
  sendEvent("onStreamToken", mapOf(
70
90
  "sessionId" to sessionId,
71
91
  "token" to token,
@@ -77,6 +97,13 @@ class ExpoAiKitModule : Module() {
77
97
  activeStreamJobs.remove(sessionId)
78
98
  }
79
99
  }
100
+
101
+ // Route to active model
102
+ if (activeModelId == "mlkit") {
103
+ promptClient.generateTextStream(conversationPrompt, systemPrompt, streamCallback)
104
+ } else {
105
+ gemmaClient.generateTextStream(conversationPrompt, systemPrompt, streamCallback)
106
+ }
80
107
  }
81
108
 
82
109
  activeStreamJobs[sessionId] = job
@@ -86,5 +113,157 @@ class ExpoAiKitModule : Module() {
86
113
  activeStreamJobs[sessionId]?.cancel()
87
114
  activeStreamJobs.remove(sessionId)
88
115
  }
116
+
117
+ // ==================================================================
118
+ // Model discovery
119
+ // ==================================================================
120
+
121
+ Function("getBuiltInModels") {
122
+ listOf(
123
+ mapOf(
124
+ "id" to "mlkit",
125
+ "name" to "ML Kit Prompt API",
126
+ "available" to promptClient.isAvailableBlocking(),
127
+ "platform" to "android",
128
+ // ML Kit doesn't expose a context window; use a reasonable default
129
+ "contextWindow" to 4096
130
+ )
131
+ )
132
+ }
133
+
134
+ Function("getDownloadableModelStatus") { modelId: String ->
135
+ // Status reflects runtime state: "ready" if loaded in memory,
136
+ // "not-downloaded" otherwise (even if file is on disk -- setModel
137
+ // is the gatekeeper that transitions through loading -> ready).
138
+ when {
139
+ gemmaClient.getLoadedModelId() == modelId && gemmaClient.isModelLoaded() -> "ready"
140
+ else -> "not-downloaded"
141
+ }
142
+ }
143
+
144
+ Function("getDeviceRamBytes") {
145
+ val activityManager = appContext.reactContext?.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager
146
+ if (activityManager != null) {
147
+ val memInfo = ActivityManager.MemoryInfo()
148
+ activityManager.getMemoryInfo(memInfo)
149
+ memInfo.totalMem
150
+ } else {
151
+ 0L
152
+ }
153
+ }
154
+
155
+ // ==================================================================
156
+ // Model selection & memory management
157
+ // ==================================================================
158
+
159
+ AsyncFunction("setModel") Coroutine { modelId: String ->
160
+ if (modelId == "mlkit") {
161
+ // Switch to built-in: unload any Gemma model
162
+ if (gemmaClient.isModelLoaded()) {
163
+ gemmaClient.unloadModel()
164
+ val previousId = activeModelId
165
+ if (previousId != "mlkit") {
166
+ sendEvent("onModelStateChange", mapOf(
167
+ "modelId" to previousId,
168
+ "status" to "not-downloaded"
169
+ ))
170
+ }
171
+ }
172
+ activeModelId = "mlkit"
173
+ return@Coroutine
174
+ }
175
+
176
+ // Downloadable model: verify file exists
177
+ if (!gemmaClient.isModelFileDownloaded(modelId)) {
178
+ throw RuntimeException("MODEL_NOT_DOWNLOADED:$modelId:Model file not found on disk")
179
+ }
180
+
181
+ // Emit loading state
182
+ sendEvent("onModelStateChange", mapOf(
183
+ "modelId" to modelId,
184
+ "status" to "loading"
185
+ ))
186
+
187
+ try {
188
+ val modelPath = gemmaClient.getModelFilePath(modelId)
189
+ gemmaClient.loadModel(modelId, modelPath)
190
+ activeModelId = modelId
191
+
192
+ // Emit ready state
193
+ sendEvent("onModelStateChange", mapOf(
194
+ "modelId" to modelId,
195
+ "status" to "ready"
196
+ ))
197
+ } catch (e: Exception) {
198
+ // Emit failure -- revert status
199
+ sendEvent("onModelStateChange", mapOf(
200
+ "modelId" to modelId,
201
+ "status" to "not-downloaded"
202
+ ))
203
+ throw e
204
+ }
205
+ }
206
+
207
+ Function("getActiveModel") {
208
+ activeModelId
209
+ }
210
+
211
+ AsyncFunction("unloadModel") Coroutine {
212
+ if (activeModelId != "mlkit" && gemmaClient.isModelLoaded()) {
213
+ val previousId = activeModelId
214
+ gemmaClient.unloadModel()
215
+ activeModelId = "mlkit"
216
+ sendEvent("onModelStateChange", mapOf(
217
+ "modelId" to previousId,
218
+ "status" to "not-downloaded"
219
+ ))
220
+ }
221
+ }
222
+
223
+ // ==================================================================
224
+ // Model lifecycle (downloadable models only)
225
+ // ==================================================================
226
+
227
+ AsyncFunction("downloadModel") Coroutine { modelId: String, url: String, sha256: String ->
228
+ sendEvent("onModelStateChange", mapOf(
229
+ "modelId" to modelId,
230
+ "status" to "downloading"
231
+ ))
232
+
233
+ try {
234
+ gemmaClient.downloadModelFile(modelId, url, sha256) { bytesRead, totalBytes ->
235
+ sendEvent("onDownloadProgress", mapOf(
236
+ "modelId" to modelId,
237
+ "progress" to if (totalBytes > 0) bytesRead.toDouble() / totalBytes else 0.0
238
+ ))
239
+ }
240
+
241
+ // Download complete -- file is on disk but not loaded
242
+ sendEvent("onModelStateChange", mapOf(
243
+ "modelId" to modelId,
244
+ "status" to "not-downloaded"
245
+ ))
246
+ } catch (e: Exception) {
247
+ sendEvent("onModelStateChange", mapOf(
248
+ "modelId" to modelId,
249
+ "status" to "not-downloaded"
250
+ ))
251
+ throw e
252
+ }
253
+ }
254
+
255
+ AsyncFunction("deleteModel") Coroutine { modelId: String ->
256
+ // If this model is active, switch back to mlkit first
257
+ if (activeModelId == modelId) {
258
+ activeModelId = "mlkit"
259
+ }
260
+
261
+ gemmaClient.deleteModelFile(modelId)
262
+
263
+ sendEvent("onModelStateChange", mapOf(
264
+ "modelId" to modelId,
265
+ "status" to "not-downloaded"
266
+ ))
267
+ }
89
268
  }
90
269
  }
@@ -0,0 +1,312 @@
1
+ package expo.modules.aikit
2
+
3
+ import android.content.Context
4
+ import com.google.mediapipe.tasks.genai.llminference.LlmInference
5
+ import kotlinx.coroutines.CompletableDeferred
6
+ import kotlinx.coroutines.Dispatchers
7
+ import kotlinx.coroutines.sync.Mutex
8
+ import kotlinx.coroutines.sync.withLock
9
+ import kotlinx.coroutines.withContext
10
+ import java.io.File
11
+ import java.io.FileInputStream
12
+ import java.io.FileOutputStream
13
+ import java.io.IOException
14
+ import java.net.HttpURLConnection
15
+ import java.net.URL
16
+ import java.security.MessageDigest
17
+
18
+ /**
19
+ * Wrapper around MediaPipe LlmInference for Gemma 4 models.
20
+ *
21
+ * Concurrency model:
22
+ * - A Mutex guards all state transitions (load, unload, inference).
23
+ * - sendMessage/startStreaming block on the mutex if a load is in progress.
24
+ * - deleteModel waits for inference to finish, then unloads and deletes.
25
+ * - A separate isDownloading flag prevents concurrent downloads (checked before
26
+ * the long-running download, not inside the mutex).
27
+ */
28
+ class GemmaInferenceClient(private val context: Context) {
29
+
30
+ private val mutex = Mutex()
31
+ private var llmInference: LlmInference? = null
32
+ private var loadedModelId: String? = null
33
+
34
+ @Volatile
35
+ private var isDownloading = false
36
+
37
+ // -------------------------------------------------------------------------
38
+ // Model lifecycle
39
+ // -------------------------------------------------------------------------
40
+
41
+ /**
42
+ * Load a model into memory. Unloads any previously loaded model first.
43
+ * Caller is responsible for emitting onModelStateChange events.
44
+ */
45
+ suspend fun loadModel(modelId: String, modelPath: String) = mutex.withLock {
46
+ // Unload previous model if different
47
+ if (loadedModelId != null && loadedModelId != modelId) {
48
+ llmInference?.close()
49
+ llmInference = null
50
+ loadedModelId = null
51
+ }
52
+
53
+ if (loadedModelId == modelId && llmInference != null) {
54
+ return@withLock // Already loaded
55
+ }
56
+
57
+ try {
58
+ val options = LlmInference.LlmInferenceOptions.builder()
59
+ .setModelPath(modelPath)
60
+ .build()
61
+ llmInference = LlmInference.createFromOptions(context, options)
62
+ loadedModelId = modelId
63
+ } catch (e: OutOfMemoryError) {
64
+ llmInference = null
65
+ loadedModelId = null
66
+ throw RuntimeException("INFERENCE_OOM:$modelId:Device does not have enough memory to load model")
67
+ } catch (e: Exception) {
68
+ llmInference = null
69
+ loadedModelId = null
70
+ throw RuntimeException("MODEL_LOAD_FAILED:$modelId:${e.message}")
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Unload the current model from memory.
76
+ */
77
+ suspend fun unloadModel() = mutex.withLock {
78
+ llmInference?.close()
79
+ llmInference = null
80
+ loadedModelId = null
81
+ }
82
+
83
+ fun getLoadedModelId(): String? = loadedModelId
84
+
85
+ fun isModelLoaded(): Boolean = llmInference != null
86
+
87
+ // -------------------------------------------------------------------------
88
+ // Inference
89
+ // -------------------------------------------------------------------------
90
+
91
+ /**
92
+ * Generate a complete response. Blocks until done.
93
+ * The mutex ensures this cannot run concurrently with load/unload.
94
+ */
95
+ suspend fun generateText(prompt: String, systemPrompt: String): String = mutex.withLock {
96
+ val inference = llmInference
97
+ ?: throw RuntimeException("MODEL_NOT_DOWNLOADED:${loadedModelId ?: "unknown"}:No model loaded")
98
+
99
+ val fullPrompt = buildFullPrompt(prompt, systemPrompt)
100
+
101
+ try {
102
+ withContext(Dispatchers.IO) {
103
+ inference.generateResponse(fullPrompt)
104
+ }
105
+ } catch (e: OutOfMemoryError) {
106
+ throw RuntimeException("INFERENCE_OOM:${loadedModelId ?: "unknown"}:Out of memory during inference")
107
+ } catch (e: Exception) {
108
+ throw RuntimeException("INFERENCE_FAILED:${loadedModelId ?: "unknown"}:${e.message}")
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Generate a streaming response. The onChunk callback receives
114
+ * (token=delta, accumulatedText=full, isDone) matching the PromptApiClient contract.
115
+ *
116
+ * MediaPipe's generateResponseAsync passes accumulated text in its partial result
117
+ * listener, so we diff against previousText to extract the delta token.
118
+ *
119
+ * We use a CompletableDeferred to keep the mutex held until streaming completes,
120
+ * preventing concurrent load/unload during active inference.
121
+ */
122
+ suspend fun generateTextStream(
123
+ prompt: String,
124
+ systemPrompt: String,
125
+ onChunk: (token: String, accumulatedText: String, isDone: Boolean) -> Unit
126
+ ) = mutex.withLock {
127
+ val inference = llmInference
128
+ ?: throw RuntimeException("MODEL_NOT_DOWNLOADED:${loadedModelId ?: "unknown"}:No model loaded")
129
+
130
+ val fullPrompt = buildFullPrompt(prompt, systemPrompt)
131
+
132
+ try {
133
+ withContext(Dispatchers.IO) {
134
+ val completion = CompletableDeferred<String>()
135
+ var previousText = ""
136
+
137
+ // MediaPipe streaming: generateResponseAsync calls the listener with
138
+ // accumulated text (not deltas). We normalize to match PromptApiClient's
139
+ // (token=delta, accumulatedText=full, isDone) contract.
140
+ inference.generateResponseAsync(fullPrompt) { partialResult, done ->
141
+ val accumulated = partialResult ?: ""
142
+ val token = if (accumulated.length > previousText.length) {
143
+ accumulated.substring(previousText.length)
144
+ } else {
145
+ ""
146
+ }
147
+ previousText = accumulated
148
+ onChunk(token, accumulated, done)
149
+
150
+ if (done) {
151
+ completion.complete(accumulated)
152
+ }
153
+ }
154
+
155
+ // Wait until streaming finishes so the mutex stays held
156
+ completion.await()
157
+ }
158
+ } catch (e: OutOfMemoryError) {
159
+ throw RuntimeException("INFERENCE_OOM:${loadedModelId ?: "unknown"}:Out of memory during inference")
160
+ } catch (e: Exception) {
161
+ throw RuntimeException("INFERENCE_FAILED:${loadedModelId ?: "unknown"}:${e.message}")
162
+ }
163
+ }
164
+
165
+ // -------------------------------------------------------------------------
166
+ // Download
167
+ // -------------------------------------------------------------------------
168
+
169
+ /**
170
+ * Download a model file with progress reporting.
171
+ * Prevents concurrent downloads. On failure, deletes partial files.
172
+ *
173
+ * Strategy: restart from scratch on failure (no HTTP Range resumption).
174
+ * Downloads to a .tmp file, atomically renames on success.
175
+ */
176
+ suspend fun downloadModelFile(
177
+ modelId: String,
178
+ url: String,
179
+ sha256: String,
180
+ onProgress: (bytesRead: Long, totalBytes: Long) -> Unit
181
+ ) {
182
+ if (isDownloading) {
183
+ throw RuntimeException("DOWNLOAD_FAILED:$modelId:Download already in progress")
184
+ }
185
+ isDownloading = true
186
+
187
+ try {
188
+ withContext(Dispatchers.IO) {
189
+ val modelsDir = File(context.filesDir, "models")
190
+ modelsDir.mkdirs()
191
+
192
+ val targetFile = File(modelsDir, "$modelId.gguf")
193
+ val tempFile = File(modelsDir, "$modelId.gguf.tmp")
194
+
195
+ try {
196
+ val connection = URL(url).openConnection() as HttpURLConnection
197
+ connection.connectTimeout = 30_000
198
+ connection.readTimeout = 30_000
199
+ connection.connect()
200
+
201
+ if (connection.responseCode != HttpURLConnection.HTTP_OK) {
202
+ throw IOException("HTTP ${connection.responseCode}: ${connection.responseMessage}")
203
+ }
204
+
205
+ val totalBytes = connection.contentLengthLong
206
+ var bytesRead = 0L
207
+
208
+ connection.inputStream.use { input ->
209
+ FileOutputStream(tempFile).use { output ->
210
+ val buffer = ByteArray(8192)
211
+ var read: Int
212
+ while (input.read(buffer).also { read = it } != -1) {
213
+ output.write(buffer, 0, read)
214
+ bytesRead += read
215
+ if (totalBytes > 0) {
216
+ onProgress(bytesRead, totalBytes)
217
+ }
218
+ }
219
+ }
220
+ }
221
+
222
+ // Verify SHA256 if provided
223
+ if (sha256.isNotEmpty()) {
224
+ val actualHash = computeSha256(tempFile)
225
+ if (!actualHash.equals(sha256, ignoreCase = true)) {
226
+ tempFile.delete()
227
+ throw RuntimeException("DOWNLOAD_CORRUPT:$modelId:SHA256 mismatch: expected $sha256, got $actualHash")
228
+ }
229
+ }
230
+
231
+ // Atomic rename
232
+ if (!tempFile.renameTo(targetFile)) {
233
+ tempFile.delete()
234
+ throw IOException("Failed to rename temp file to target")
235
+ }
236
+ } catch (e: Exception) {
237
+ // Always clean up partial file
238
+ tempFile.delete()
239
+ when {
240
+ e is RuntimeException && e.message?.startsWith("DOWNLOAD_CORRUPT") == true -> throw e
241
+ context.filesDir.freeSpace < 100_000_000 ->
242
+ throw RuntimeException("DOWNLOAD_STORAGE_FULL:$modelId:Insufficient disk space")
243
+ else ->
244
+ throw RuntimeException("DOWNLOAD_FAILED:$modelId:${e.message}")
245
+ }
246
+ }
247
+ }
248
+ } finally {
249
+ isDownloading = false
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Delete a model file from disk. If the model is loaded, unloads it first.
255
+ */
256
+ suspend fun deleteModelFile(modelId: String) = mutex.withLock {
257
+ // Unload if this model is currently loaded
258
+ if (loadedModelId == modelId) {
259
+ llmInference?.close()
260
+ llmInference = null
261
+ loadedModelId = null
262
+ }
263
+
264
+ val modelFile = File(context.filesDir, "models/$modelId.gguf")
265
+ if (modelFile.exists()) {
266
+ modelFile.delete()
267
+ }
268
+ // Also clean up any partial downloads
269
+ val tempFile = File(context.filesDir, "models/$modelId.gguf.tmp")
270
+ if (tempFile.exists()) {
271
+ tempFile.delete()
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Check if a model file exists on disk.
277
+ */
278
+ fun isModelFileDownloaded(modelId: String): Boolean {
279
+ return File(context.filesDir, "models/$modelId.gguf").exists()
280
+ }
281
+
282
+ /**
283
+ * Get the file path for a downloaded model.
284
+ */
285
+ fun getModelFilePath(modelId: String): String {
286
+ return File(context.filesDir, "models/$modelId.gguf").absolutePath
287
+ }
288
+
289
+ // -------------------------------------------------------------------------
290
+ // Private helpers
291
+ // -------------------------------------------------------------------------
292
+
293
+ private fun buildFullPrompt(prompt: String, systemPrompt: String): String {
294
+ return if (systemPrompt.isNotBlank()) {
295
+ "$systemPrompt\n\n$prompt"
296
+ } else {
297
+ prompt
298
+ }
299
+ }
300
+
301
+ private fun computeSha256(file: File): String {
302
+ val digest = MessageDigest.getInstance("SHA-256")
303
+ FileInputStream(file).use { fis ->
304
+ val buffer = ByteArray(8192)
305
+ var read: Int
306
+ while (fis.read(buffer).also { read = it } != -1) {
307
+ digest.update(buffer, 0, read)
308
+ }
309
+ }
310
+ return digest.digest().joinToString("") { "%02x".format(it) }
311
+ }
312
+ }
@@ -12,6 +12,7 @@ export interface ExpoAiKitNativeModule {
12
12
  stopStreaming(sessionId: string): Promise<void>;
13
13
  getBuiltInModels(): BuiltInModel[];
14
14
  getDownloadableModelStatus(modelId: string): DownloadableModelStatus;
15
+ getDeviceRamBytes(): number;
15
16
  setModel(modelId: string): Promise<void>;
16
17
  getActiveModel(): string;
17
18
  unloadModel(): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoAiKitModule.d.ts","sourceRoot":"","sources":["../src/ExpoAiKitModule.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EACL,UAAU,EACV,WAAW,EACX,cAAc,EACd,YAAY,EACZ,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,qBAAqB,GAAG;IAClC,aAAa,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC/C,kBAAkB,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,IAAI,CAAC;IAChE,kBAAkB,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;CAC5D,CAAC;AAEF,MAAM,WAAW,qBAAqB;IAEpC,WAAW,IAAI,OAAO,CAAC;IACvB,WAAW,CACT,QAAQ,EAAE,UAAU,EAAE,EACtB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,WAAW,CAAC,CAAC;IACxB,cAAc,CACZ,QAAQ,EAAE,UAAU,EAAE,EACtB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAGhD,gBAAgB,IAAI,YAAY,EAAE,CAAC;IACnC,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,uBAAuB,CAAC;IAKrE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,cAAc,IAAI,MAAM,CAAC;IAGzB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAG7B,aAAa,CACX,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAG5C,WAAW,CAAC,CAAC,SAAS,MAAM,qBAAqB,EAC/C,SAAS,EAAE,CAAC,EACZ,QAAQ,EAAE,qBAAqB,CAAC,CAAC,CAAC,GACjC,iBAAiB,CAAC;CACtB;AAED,QAAA,MAAM,eAAe,uBACoC,CAAC;AAE1D,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"ExpoAiKitModule.d.ts","sourceRoot":"","sources":["../src/ExpoAiKitModule.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EACL,UAAU,EACV,WAAW,EACX,cAAc,EACd,YAAY,EACZ,uBAAuB,EACvB,0BAA0B,EAC1B,qBAAqB,EACtB,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,qBAAqB,GAAG;IAClC,aAAa,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC/C,kBAAkB,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,IAAI,CAAC;IAChE,kBAAkB,EAAE,CAAC,KAAK,EAAE,qBAAqB,KAAK,IAAI,CAAC;CAC5D,CAAC;AAEF,MAAM,WAAW,qBAAqB;IAEpC,WAAW,IAAI,OAAO,CAAC;IACvB,WAAW,CACT,QAAQ,EAAE,UAAU,EAAE,EACtB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,WAAW,CAAC,CAAC;IACxB,cAAc,CACZ,QAAQ,EAAE,UAAU,EAAE,EACtB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAGhD,gBAAgB,IAAI,YAAY,EAAE,CAAC;IACnC,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,uBAAuB,CAAC;IACrE,iBAAiB,IAAI,MAAM,CAAC;IAK5B,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,cAAc,IAAI,MAAM,CAAC;IAGzB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAG7B,aAAa,CACX,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAG5C,WAAW,CAAC,CAAC,SAAS,MAAM,qBAAqB,EAC/C,SAAS,EAAE,CAAC,EACZ,QAAQ,EAAE,qBAAqB,CAAC,CAAC,CAAC,GACjC,iBAAiB,CAAC;CACtB;AAED,QAAA,MAAM,eAAe,uBACoC,CAAC;AAE1D,eAAe,eAAe,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoAiKitModule.js","sourceRoot":"","sources":["../src/ExpoAiKitModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AA4DxD,MAAM,eAAe,GACnB,mBAAmB,CAAwB,WAAW,CAAC,CAAC;AAE1D,eAAe,eAAe,CAAC","sourcesContent":["import { requireNativeModule } from 'expo-modules-core';\nimport type { EventSubscription } from 'expo-modules-core';\nimport {\n LLMMessage,\n LLMResponse,\n LLMStreamEvent,\n BuiltInModel,\n DownloadableModelStatus,\n ModelDownloadProgressEvent,\n ModelStateChangeEvent,\n} from './types';\n\nexport type ExpoAiKitModuleEvents = {\n onStreamToken: (event: LLMStreamEvent) => void;\n onDownloadProgress: (event: ModelDownloadProgressEvent) => void;\n onModelStateChange: (event: ModelStateChangeEvent) => void;\n};\n\nexport interface ExpoAiKitNativeModule {\n // Existing inference API\n isAvailable(): boolean;\n sendMessage(\n messages: LLMMessage[],\n systemPrompt: string\n ): Promise<LLMResponse>;\n startStreaming(\n messages: LLMMessage[],\n systemPrompt: string,\n sessionId: string\n ): Promise<void>;\n stopStreaming(sessionId: string): Promise<void>;\n\n // Model discovery\n getBuiltInModels(): BuiltInModel[];\n getDownloadableModelStatus(modelId: string): DownloadableModelStatus;\n\n // Model selection & memory management\n // setModel is async: switching to a downloadable model loads it into memory.\n // Auto-unloads the previous downloadable model (only one loaded at a time).\n setModel(modelId: string): Promise<void>;\n getActiveModel(): string;\n // Explicitly free memory from the loaded downloadable model.\n // Reverts to the platform built-in model.\n unloadModel(): Promise<void>;\n\n // Model lifecycle (downloadable models only)\n downloadModel(\n modelId: string,\n url: string,\n sha256: string\n ): Promise<void>;\n deleteModel(modelId: string): Promise<void>;\n\n // Event subscription\n addListener<K extends keyof ExpoAiKitModuleEvents>(\n eventName: K,\n listener: ExpoAiKitModuleEvents[K]\n ): EventSubscription;\n}\n\nconst ExpoAiKitModule =\n requireNativeModule<ExpoAiKitNativeModule>('ExpoAiKit');\n\nexport default ExpoAiKitModule;\n"]}
1
+ {"version":3,"file":"ExpoAiKitModule.js","sourceRoot":"","sources":["../src/ExpoAiKitModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AA6DxD,MAAM,eAAe,GACnB,mBAAmB,CAAwB,WAAW,CAAC,CAAC;AAE1D,eAAe,eAAe,CAAC","sourcesContent":["import { requireNativeModule } from 'expo-modules-core';\nimport type { EventSubscription } from 'expo-modules-core';\nimport {\n LLMMessage,\n LLMResponse,\n LLMStreamEvent,\n BuiltInModel,\n DownloadableModelStatus,\n ModelDownloadProgressEvent,\n ModelStateChangeEvent,\n} from './types';\n\nexport type ExpoAiKitModuleEvents = {\n onStreamToken: (event: LLMStreamEvent) => void;\n onDownloadProgress: (event: ModelDownloadProgressEvent) => void;\n onModelStateChange: (event: ModelStateChangeEvent) => void;\n};\n\nexport interface ExpoAiKitNativeModule {\n // Existing inference API\n isAvailable(): boolean;\n sendMessage(\n messages: LLMMessage[],\n systemPrompt: string\n ): Promise<LLMResponse>;\n startStreaming(\n messages: LLMMessage[],\n systemPrompt: string,\n sessionId: string\n ): Promise<void>;\n stopStreaming(sessionId: string): Promise<void>;\n\n // Model discovery\n getBuiltInModels(): BuiltInModel[];\n getDownloadableModelStatus(modelId: string): DownloadableModelStatus;\n getDeviceRamBytes(): number;\n\n // Model selection & memory management\n // setModel is async: switching to a downloadable model loads it into memory.\n // Auto-unloads the previous downloadable model (only one loaded at a time).\n setModel(modelId: string): Promise<void>;\n getActiveModel(): string;\n // Explicitly free memory from the loaded downloadable model.\n // Reverts to the platform built-in model.\n unloadModel(): Promise<void>;\n\n // Model lifecycle (downloadable models only)\n downloadModel(\n modelId: string,\n url: string,\n sha256: string\n ): Promise<void>;\n deleteModel(modelId: string): Promise<void>;\n\n // Event subscription\n addListener<K extends keyof ExpoAiKitModuleEvents>(\n eventName: K,\n listener: ExpoAiKitModuleEvents[K]\n ): EventSubscription;\n}\n\nconst ExpoAiKitModule =\n requireNativeModule<ExpoAiKitNativeModule>('ExpoAiKit');\n\nexport default ExpoAiKitModule;\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/build/index.js CHANGED
@@ -923,6 +923,13 @@ export async function getDownloadableModels() {
923
923
  return [];
924
924
  }
925
925
  const platformModels = MODEL_REGISTRY.filter((entry) => entry.supportedPlatforms.includes(Platform.OS));
926
+ let deviceRamBytes = 0;
927
+ try {
928
+ deviceRamBytes = ExpoAiKitModule.getDeviceRamBytes();
929
+ }
930
+ catch {
931
+ // Native call unavailable -- default to 0 (all models will show meetsRequirements: false)
932
+ }
926
933
  return platformModels.map((entry) => {
927
934
  const status = ExpoAiKitModule.getDownloadableModelStatus(entry.id);
928
935
  return {
@@ -932,6 +939,7 @@ export async function getDownloadableModels() {
932
939
  sizeBytes: entry.sizeBytes,
933
940
  contextWindow: entry.contextWindow,
934
941
  minRamBytes: entry.minRamBytes,
942
+ meetsRequirements: deviceRamBytes >= entry.minRamBytes,
935
943
  status,
936
944
  };
937
945
  });
@@ -959,6 +967,17 @@ export async function downloadModel(modelId, options) {
959
967
  if (!entry.supportedPlatforms.includes(Platform.OS)) {
960
968
  throw new ModelError('DEVICE_NOT_SUPPORTED', modelId, `Model ${modelId} is not supported on ${Platform.OS}`);
961
969
  }
970
+ try {
971
+ const deviceRamBytes = ExpoAiKitModule.getDeviceRamBytes();
972
+ if (deviceRamBytes < entry.minRamBytes) {
973
+ throw new ModelError('DEVICE_NOT_SUPPORTED', modelId, `Device has ${Math.round(deviceRamBytes / 1e9)}GB RAM, model requires ${Math.round(entry.minRamBytes / 1e9)}GB`);
974
+ }
975
+ }
976
+ catch (e) {
977
+ if (e instanceof ModelError)
978
+ throw e;
979
+ // If getDeviceRamBytes is unavailable, skip the check
980
+ }
962
981
  let subscription;
963
982
  if (options?.onProgress) {
964
983
  subscription = ExpoAiKitModule.addListener('onDownloadProgress', (event) => {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import ExpoAiKitModule from './ExpoAiKitModule';\nimport { Platform } from 'react-native';\nimport {\n LLMMessage,\n LLMSendOptions,\n LLMResponse,\n LLMStreamOptions,\n LLMStreamEvent,\n LLMStreamCallback,\n LLMSummarizeOptions,\n LLMTranslateOptions,\n LLMRewriteOptions,\n LLMExtractKeyPointsOptions,\n LLMAnswerQuestionOptions,\n LLMSuggestOptions,\n LLMSuggestResponse,\n LLMSmartReplyOptions,\n LLMAutocompleteOptions,\n BuiltInModel,\n DownloadableModel,\n ModelError,\n} from './types';\nimport { MODEL_REGISTRY, getRegistryEntry } from './models';\n\nexport * from './types';\nexport * from './memory';\nexport * from './hooks';\nexport * from './models';\n\nconst DEFAULT_SYSTEM_PROMPT =\n 'You are a helpful, friendly assistant. Answer the user directly and concisely.';\n\nlet streamIdCounter = 0;\nfunction generateSessionId(): string {\n return `stream_${Date.now()}_${++streamIdCounter}`;\n}\n\n// ============================================================================\n// Prompt Helper Constants\n// ============================================================================\n\nconst SUMMARIZE_LENGTH_INSTRUCTIONS = {\n short: 'Keep it very brief, around 1-2 sentences.',\n medium: 'Provide a moderate summary, around 3-5 sentences.',\n long: 'Provide a comprehensive summary covering all main points.',\n} as const;\n\nconst SUMMARIZE_STYLE_INSTRUCTIONS = {\n paragraph: 'Write the summary as a flowing paragraph.',\n bullets: 'Format the summary as bullet points.',\n tldr: 'Start with \"TL;DR:\" and give an extremely concise summary in 1 sentence.',\n} as const;\n\nconst TRANSLATE_TONE_INSTRUCTIONS = {\n formal: 'Use formal language and honorifics where appropriate.',\n informal: 'Use casual, everyday language.',\n neutral: 'Use standard, neutral language.',\n} as const;\n\nconst REWRITE_STYLE_INSTRUCTIONS = {\n formal:\n 'Rewrite in a formal, professional tone suitable for business communication.',\n casual: 'Rewrite in a casual, conversational tone.',\n professional:\n 'Rewrite in a clear, professional tone suitable for work contexts.',\n friendly: 'Rewrite in a warm, friendly tone.',\n concise:\n 'Rewrite to be as brief as possible while keeping the meaning intact.',\n detailed: 'Expand and add more detail and explanation.',\n simple:\n 'Rewrite using simple words and short sentences, easy for anyone to understand.',\n academic: 'Rewrite in an academic style suitable for scholarly writing.',\n} as const;\n\nconst SUGGEST_TONE_INSTRUCTIONS = {\n formal: 'Use formal, professional language.',\n casual: 'Use casual, everyday language.',\n professional: 'Use clear, professional language suitable for work.',\n friendly: 'Use warm, friendly language.',\n neutral: 'Use standard, neutral language.',\n} as const;\n\nconst ANSWER_DETAIL_INSTRUCTIONS = {\n brief: 'Give a brief, direct answer in 1-2 sentences.',\n medium: 'Provide a clear answer with some explanation.',\n detailed:\n 'Provide a comprehensive answer with full explanation and relevant details from the context.',\n} as const;\n\n// ============================================================================\n// Prompt Builder Helpers\n// ============================================================================\n\nfunction buildSummarizePrompt(\n length: 'short' | 'medium' | 'long',\n style: 'paragraph' | 'bullets' | 'tldr'\n): string {\n return `You are a summarization assistant. Summarize the provided text accurately and concisely. ${SUMMARIZE_LENGTH_INSTRUCTIONS[length]} ${SUMMARIZE_STYLE_INSTRUCTIONS[style]} Only output the summary, nothing else.`;\n}\n\nfunction buildTranslatePrompt(\n to: string,\n from: string | undefined,\n tone: 'formal' | 'informal' | 'neutral'\n): string {\n const fromClause = from ? `from ${from} ` : '';\n return `You are a translation assistant. Translate the provided text ${fromClause}to ${to}. ${TRANSLATE_TONE_INSTRUCTIONS[tone]} Only output the translation, nothing else. Do not include any explanations or notes.`;\n}\n\nfunction buildRewritePrompt(\n style:\n | 'formal'\n | 'casual'\n | 'professional'\n | 'friendly'\n | 'concise'\n | 'detailed'\n | 'simple'\n | 'academic'\n): string {\n return `You are a writing assistant. ${REWRITE_STYLE_INSTRUCTIONS[style]} Preserve the original meaning. Only output the rewritten text, nothing else.`;\n}\n\nfunction buildExtractKeyPointsPrompt(maxPoints: number): string {\n return `You are an analysis assistant. Extract the ${maxPoints} most important key points from the provided text. Format each point as a bullet point starting with \"•\". Be concise and focus on the most significant information. Only output the bullet points, nothing else.`;\n}\n\nfunction buildSuggestPrompt(\n count: number,\n tone: 'formal' | 'casual' | 'professional' | 'friendly' | 'neutral',\n context?: string\n): string {\n const contextClause = context\n ? ` The user is writing in this context: \"${context}\".`\n : '';\n return `You are a text suggestion assistant. Given the user's partial text, generate exactly ${count} possible continuations or completions.${contextClause} ${SUGGEST_TONE_INSTRUCTIONS[tone]} Output ONLY the suggestions, one per line, numbered like \"1. suggestion here\". Do not include any other text or explanation.`;\n}\n\nfunction buildSmartReplyPrompt(\n count: number,\n tone: 'formal' | 'casual' | 'professional' | 'friendly' | 'neutral',\n persona?: string\n): string {\n const personaClause = persona ? ` You are replying as: ${persona}.` : '';\n return `You are a smart reply assistant. Given a conversation, generate exactly ${count} short, contextually appropriate reply suggestions that the user could send as their next message.${personaClause} ${SUGGEST_TONE_INSTRUCTIONS[tone]} Each reply should be a complete, ready-to-send message. Output ONLY the replies, one per line, numbered like \"1. reply here\". Do not include any other text or explanation.`;\n}\n\nfunction buildAutocompletePrompt(\n count: number,\n maxWords: number,\n context?: string\n): string {\n const contextClause = context\n ? ` The user is writing about: \"${context}\".`\n : '';\n return `You are an autocomplete assistant. Given the user's partial text, generate exactly ${count} natural completions of the current sentence or phrase. Each completion should be at most ${maxWords} words and should seamlessly continue from the user's text.${contextClause} Output ONLY the completions, one per line, numbered like \"1. completion here\". Do not repeat the user's text. Do not include any other text or explanation.`;\n}\n\nfunction parseSuggestions(raw: string): { text: string }[] {\n return raw\n .split('\\n')\n .map((line) => line.replace(/^\\d+[\\.\\)]\\s*/, '').trim())\n .filter((line) => line.length > 0)\n .map((text) => ({ text }));\n}\n\nfunction buildAnswerQuestionPrompt(\n detail: 'brief' | 'medium' | 'detailed'\n): string {\n return `You are a question-answering assistant. Answer questions based ONLY on the provided context. ${ANSWER_DETAIL_INSTRUCTIONS[detail]} If the answer cannot be found in the context, say so. Do not make up information.`;\n}\n\n/**\n * Check if on-device AI is available on the current device.\n * Returns false on unsupported platforms (web, etc.).\n */\nexport async function isAvailable(): Promise<boolean> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return false;\n }\n return ExpoAiKitModule.isAvailable();\n}\n\n/**\n * Send messages to the on-device LLM and get a response.\n *\n * @param messages - Array of messages representing the conversation\n * @param options - Optional settings (systemPrompt fallback)\n * @returns Promise with the generated response\n *\n * @example\n * ```ts\n * const response = await sendMessage([\n * { role: 'user', content: 'What is 2 + 2?' }\n * ]);\n * console.log(response.text); // \"4\"\n * ```\n *\n * @example\n * ```ts\n * // With system prompt\n * const response = await sendMessage(\n * [{ role: 'user', content: 'Hello!' }],\n * { systemPrompt: 'You are a pirate. Respond in pirate speak.' }\n * );\n * ```\n *\n * @example\n * ```ts\n * // Multi-turn conversation\n * const response = await sendMessage([\n * { role: 'system', content: 'You are a helpful assistant.' },\n * { role: 'user', content: 'My name is Alice.' },\n * { role: 'assistant', content: 'Nice to meet you, Alice!' },\n * { role: 'user', content: 'What is my name?' }\n * ]);\n * ```\n */\nexport async function sendMessage(\n messages: LLMMessage[],\n options?: LLMSendOptions\n): Promise<LLMResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { text: '' };\n }\n\n if (!messages || messages.length === 0) {\n throw new Error('messages array cannot be empty');\n }\n\n // Determine system prompt: use from messages array if present, else options, else default\n const hasSystemMessage = messages.some((m) => m.role === 'system');\n const systemPrompt = hasSystemMessage\n ? '' // Native will extract from messages\n : options?.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;\n\n return ExpoAiKitModule.sendMessage(messages, systemPrompt);\n}\n\n/**\n * Stream messages to the on-device LLM and receive progressive token updates.\n *\n * @param messages - Array of messages representing the conversation\n * @param onToken - Callback function called for each token/chunk received\n * @param options - Optional settings (systemPrompt fallback)\n * @returns Object with stop() function to cancel streaming and promise that resolves when complete\n *\n * @example\n * ```ts\n * // Basic streaming\n * const { promise } = streamMessage(\n * [{ role: 'user', content: 'Tell me a story' }],\n * (event) => {\n * console.log(event.token); // Each token as it arrives\n * console.log(event.accumulatedText); // Full text so far\n * }\n * );\n * await promise;\n * ```\n *\n * @example\n * ```ts\n * // With cancellation\n * const { promise, stop } = streamMessage(\n * [{ role: 'user', content: 'Write a long essay' }],\n * (event) => setText(event.accumulatedText)\n * );\n *\n * // Cancel after 5 seconds\n * setTimeout(() => stop(), 5000);\n * ```\n *\n * @example\n * ```ts\n * // React state update pattern\n * const [text, setText] = useState('');\n *\n * streamMessage(\n * [{ role: 'user', content: 'Hello!' }],\n * (event) => setText(event.accumulatedText)\n * );\n * ```\n */\nexport function streamMessage(\n messages: LLMMessage[],\n onToken: LLMStreamCallback,\n options?: LLMStreamOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n // Handle unsupported platforms\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return {\n promise: Promise.resolve({ text: '' }),\n stop: () => {},\n };\n }\n\n if (!messages || messages.length === 0) {\n return {\n promise: Promise.reject(new Error('messages array cannot be empty')),\n stop: () => {},\n };\n }\n\n const sessionId = generateSessionId();\n let finalText = '';\n let stopped = false;\n\n // Determine system prompt: use from messages array if present, else options, else default\n const hasSystemMessage = messages.some((m) => m.role === 'system');\n const systemPrompt = hasSystemMessage\n ? '' // Native will extract from messages\n : options?.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;\n\n const promise = new Promise<LLMResponse>((resolve, reject) => {\n // Subscribe to stream events\n const subscription = ExpoAiKitModule.addListener(\n 'onStreamToken',\n (event: LLMStreamEvent) => {\n // Only process events for this session\n if (event.sessionId !== sessionId) return;\n\n finalText = event.accumulatedText;\n\n // Call the user's callback\n onToken(event);\n\n // If done, clean up and resolve\n if (event.isDone) {\n subscription.remove();\n resolve({ text: finalText });\n }\n }\n );\n\n // Start streaming on native side\n ExpoAiKitModule.startStreaming(messages, systemPrompt, sessionId).catch(\n (error) => {\n subscription.remove();\n reject(error);\n }\n );\n });\n\n const stop = () => {\n if (stopped) return;\n stopped = true;\n ExpoAiKitModule.stopStreaming(sessionId).catch(() => {\n // Ignore errors when stopping\n });\n };\n\n return { promise, stop };\n}\n\n// ============================================================================\n// Prompt Helpers\n// ============================================================================\n\n/**\n * Summarize text content using on-device AI.\n *\n * @param text - The text to summarize\n * @param options - Optional settings for summary style and length\n * @returns Promise with the generated summary\n *\n * @example\n * ```ts\n * // Basic summarization\n * const result = await summarize(longArticle);\n * console.log(result.text);\n * ```\n *\n * @example\n * ```ts\n * // Short bullet-point summary\n * const result = await summarize(longArticle, {\n * length: 'short',\n * style: 'bullets'\n * });\n * ```\n *\n * @example\n * ```ts\n * // TL;DR style\n * const result = await summarize(longArticle, {\n * style: 'tldr'\n * });\n * ```\n */\nexport async function summarize(\n text: string,\n options?: LLMSummarizeOptions\n): Promise<LLMResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { text: '' };\n }\n\n if (!text || text.trim().length === 0) {\n throw new Error('text cannot be empty');\n }\n\n const length = options?.length ?? 'medium';\n const style = options?.style ?? 'paragraph';\n const systemPrompt = buildSummarizePrompt(length, style);\n\n return sendMessage([{ role: 'user', content: text }], { systemPrompt });\n}\n\n/**\n * Summarize text with streaming output.\n *\n * @param text - The text to summarize\n * @param onToken - Callback for each token received\n * @param options - Optional settings for summary style and length\n * @returns Object with stop() function and promise\n *\n * @example\n * ```ts\n * const { promise } = streamSummarize(\n * longArticle,\n * (event) => setSummary(event.accumulatedText),\n * { style: 'bullets' }\n * );\n * await promise;\n * ```\n */\nexport function streamSummarize(\n text: string,\n onToken: LLMStreamCallback,\n options?: LLMSummarizeOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { promise: Promise.resolve({ text: '' }), stop: () => {} };\n }\n\n if (!text || text.trim().length === 0) {\n return {\n promise: Promise.reject(new Error('text cannot be empty')),\n stop: () => {},\n };\n }\n\n const length = options?.length ?? 'medium';\n const style = options?.style ?? 'paragraph';\n const systemPrompt = buildSummarizePrompt(length, style);\n\n return streamMessage([{ role: 'user', content: text }], onToken, {\n systemPrompt,\n });\n}\n\n/**\n * Translate text to another language using on-device AI.\n *\n * @param text - The text to translate\n * @param options - Translation options including target language\n * @returns Promise with the translated text\n *\n * @example\n * ```ts\n * // Basic translation\n * const result = await translate('Hello, world!', { to: 'Spanish' });\n * console.log(result.text); // \"¡Hola, mundo!\"\n * ```\n *\n * @example\n * ```ts\n * // Formal translation with source language\n * const result = await translate('Hey, what\\'s up?', {\n * to: 'French',\n * from: 'English',\n * tone: 'formal'\n * });\n * ```\n */\nexport async function translate(\n text: string,\n options: LLMTranslateOptions\n): Promise<LLMResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { text: '' };\n }\n\n if (!text || text.trim().length === 0) {\n throw new Error('text cannot be empty');\n }\n\n const { to, from, tone = 'neutral' } = options;\n const systemPrompt = buildTranslatePrompt(to, from, tone);\n\n return sendMessage([{ role: 'user', content: text }], { systemPrompt });\n}\n\n/**\n * Translate text with streaming output.\n *\n * @param text - The text to translate\n * @param onToken - Callback for each token received\n * @param options - Translation options including target language\n * @returns Object with stop() function and promise\n *\n * @example\n * ```ts\n * const { promise } = streamTranslate(\n * 'Hello, world!',\n * (event) => setTranslation(event.accumulatedText),\n * { to: 'Japanese' }\n * );\n * await promise;\n * ```\n */\nexport function streamTranslate(\n text: string,\n onToken: LLMStreamCallback,\n options: LLMTranslateOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { promise: Promise.resolve({ text: '' }), stop: () => {} };\n }\n\n if (!text || text.trim().length === 0) {\n return {\n promise: Promise.reject(new Error('text cannot be empty')),\n stop: () => {},\n };\n }\n\n const { to, from, tone = 'neutral' } = options;\n const systemPrompt = buildTranslatePrompt(to, from, tone);\n\n return streamMessage([{ role: 'user', content: text }], onToken, {\n systemPrompt,\n });\n}\n\n/**\n * Rewrite text in a different style using on-device AI.\n *\n * @param text - The text to rewrite\n * @param options - Rewrite options specifying the target style\n * @returns Promise with the rewritten text\n *\n * @example\n * ```ts\n * // Make text more formal\n * const result = await rewrite('hey can u help me out?', {\n * style: 'formal'\n * });\n * console.log(result.text); // \"Would you be able to assist me?\"\n * ```\n *\n * @example\n * ```ts\n * // Simplify complex text\n * const result = await rewrite(technicalText, { style: 'simple' });\n * ```\n */\nexport async function rewrite(\n text: string,\n options: LLMRewriteOptions\n): Promise<LLMResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { text: '' };\n }\n\n if (!text || text.trim().length === 0) {\n throw new Error('text cannot be empty');\n }\n\n const { style } = options;\n const systemPrompt = buildRewritePrompt(style);\n\n return sendMessage([{ role: 'user', content: text }], { systemPrompt });\n}\n\n/**\n * Rewrite text with streaming output.\n *\n * @param text - The text to rewrite\n * @param onToken - Callback for each token received\n * @param options - Rewrite options specifying the target style\n * @returns Object with stop() function and promise\n *\n * @example\n * ```ts\n * const { promise } = streamRewrite(\n * 'hey whats up',\n * (event) => setRewritten(event.accumulatedText),\n * { style: 'professional' }\n * );\n * await promise;\n * ```\n */\nexport function streamRewrite(\n text: string,\n onToken: LLMStreamCallback,\n options: LLMRewriteOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { promise: Promise.resolve({ text: '' }), stop: () => {} };\n }\n\n if (!text || text.trim().length === 0) {\n return {\n promise: Promise.reject(new Error('text cannot be empty')),\n stop: () => {},\n };\n }\n\n const { style } = options;\n const systemPrompt = buildRewritePrompt(style);\n\n return streamMessage([{ role: 'user', content: text }], onToken, {\n systemPrompt,\n });\n}\n\n/**\n * Extract key points from text using on-device AI.\n *\n * @param text - The text to extract key points from\n * @param options - Optional settings for extraction\n * @returns Promise with the key points as text\n *\n * @example\n * ```ts\n * // Extract key points from an article\n * const result = await extractKeyPoints(article);\n * console.log(result.text);\n * // \"• Point 1\\n• Point 2\\n• Point 3\"\n * ```\n *\n * @example\n * ```ts\n * // Limit to 3 key points\n * const result = await extractKeyPoints(article, { maxPoints: 3 });\n * ```\n */\nexport async function extractKeyPoints(\n text: string,\n options?: LLMExtractKeyPointsOptions\n): Promise<LLMResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { text: '' };\n }\n\n if (!text || text.trim().length === 0) {\n throw new Error('text cannot be empty');\n }\n\n const maxPoints = options?.maxPoints ?? 5;\n const systemPrompt = buildExtractKeyPointsPrompt(maxPoints);\n\n return sendMessage([{ role: 'user', content: text }], { systemPrompt });\n}\n\n/**\n * Extract key points with streaming output.\n *\n * @param text - The text to extract key points from\n * @param onToken - Callback for each token received\n * @param options - Optional settings for extraction\n * @returns Object with stop() function and promise\n *\n * @example\n * ```ts\n * const { promise } = streamExtractKeyPoints(\n * article,\n * (event) => setKeyPoints(event.accumulatedText),\n * { maxPoints: 5 }\n * );\n * await promise;\n * ```\n */\nexport function streamExtractKeyPoints(\n text: string,\n onToken: LLMStreamCallback,\n options?: LLMExtractKeyPointsOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { promise: Promise.resolve({ text: '' }), stop: () => {} };\n }\n\n if (!text || text.trim().length === 0) {\n return {\n promise: Promise.reject(new Error('text cannot be empty')),\n stop: () => {},\n };\n }\n\n const maxPoints = options?.maxPoints ?? 5;\n const systemPrompt = buildExtractKeyPointsPrompt(maxPoints);\n\n return streamMessage([{ role: 'user', content: text }], onToken, {\n systemPrompt,\n });\n}\n\n/**\n * Answer a question based on provided context using on-device AI.\n *\n * @param question - The question to answer\n * @param context - The context/document to base the answer on\n * @param options - Optional settings for the answer\n * @returns Promise with the answer\n *\n * @example\n * ```ts\n * // Answer a question about a document\n * const result = await answerQuestion(\n * 'What is the main topic?',\n * documentText\n * );\n * console.log(result.text);\n * ```\n *\n * @example\n * ```ts\n * // Get a detailed answer\n * const result = await answerQuestion(\n * 'Explain the methodology',\n * researchPaper,\n * { detail: 'detailed' }\n * );\n * ```\n */\nexport async function answerQuestion(\n question: string,\n context: string,\n options?: LLMAnswerQuestionOptions\n): Promise<LLMResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { text: '' };\n }\n\n if (!question || question.trim().length === 0) {\n throw new Error('question cannot be empty');\n }\n\n if (!context || context.trim().length === 0) {\n throw new Error('context cannot be empty');\n }\n\n const detail = options?.detail ?? 'medium';\n const systemPrompt = buildAnswerQuestionPrompt(detail);\n const userContent = `Context:\\n${context}\\n\\nQuestion: ${question}`;\n\n return sendMessage([{ role: 'user', content: userContent }], { systemPrompt });\n}\n\n/**\n * Answer a question with streaming output.\n *\n * @param question - The question to answer\n * @param context - The context/document to base the answer on\n * @param onToken - Callback for each token received\n * @param options - Optional settings for the answer\n * @returns Object with stop() function and promise\n *\n * @example\n * ```ts\n * const { promise } = streamAnswerQuestion(\n * 'What are the key findings?',\n * documentText,\n * (event) => setAnswer(event.accumulatedText),\n * { detail: 'detailed' }\n * );\n * await promise;\n * ```\n */\nexport function streamAnswerQuestion(\n question: string,\n context: string,\n onToken: LLMStreamCallback,\n options?: LLMAnswerQuestionOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { promise: Promise.resolve({ text: '' }), stop: () => {} };\n }\n\n if (!question || question.trim().length === 0) {\n return {\n promise: Promise.reject(new Error('question cannot be empty')),\n stop: () => {},\n };\n }\n\n if (!context || context.trim().length === 0) {\n return {\n promise: Promise.reject(new Error('context cannot be empty')),\n stop: () => {},\n };\n }\n\n const detail = options?.detail ?? 'medium';\n const systemPrompt = buildAnswerQuestionPrompt(detail);\n const userContent = `Context:\\n${context}\\n\\nQuestion: ${question}`;\n\n return streamMessage([{ role: 'user', content: userContent }], onToken, {\n systemPrompt,\n });\n}\n\n// ============================================================================\n// Smart Suggestions\n// ============================================================================\n\n/**\n * Generate text suggestions based on partial input using on-device AI.\n *\n * Useful for text completion, writing assistance, and predictive text features.\n *\n * @param partialText - The text the user has typed so far\n * @param options - Optional settings for suggestions\n * @returns Promise with an array of suggestions\n *\n * @example\n * ```ts\n * // Basic suggestions\n * const result = await suggest('I think we should');\n * result.suggestions.forEach(s => console.log(s.text));\n * // \"schedule a meeting to discuss this further\"\n * // \"consider an alternative approach\"\n * // \"move forward with the plan\"\n * ```\n *\n * @example\n * ```ts\n * // With context and tone\n * const result = await suggest('Dear Mr. Johnson,', {\n * count: 5,\n * context: 'writing a business email about project delays',\n * tone: 'formal'\n * });\n * ```\n */\nexport async function suggest(\n partialText: string,\n options?: LLMSuggestOptions\n): Promise<LLMSuggestResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { suggestions: [], raw: '' };\n }\n\n if (!partialText || partialText.trim().length === 0) {\n throw new Error('partialText cannot be empty');\n }\n\n const count = options?.count ?? 3;\n const tone = options?.tone ?? 'neutral';\n const systemPrompt = buildSuggestPrompt(count, tone, options?.context);\n\n const response = await sendMessage(\n [{ role: 'user', content: partialText }],\n { systemPrompt }\n );\n\n return {\n suggestions: parseSuggestions(response.text),\n raw: response.text,\n };\n}\n\n/**\n * Generate text suggestions with streaming output.\n *\n * @param partialText - The text the user has typed so far\n * @param onToken - Callback for each token received\n * @param options - Optional settings for suggestions\n * @returns Object with stop() function and promise\n *\n * @example\n * ```ts\n * const { promise } = streamSuggest(\n * 'The best way to',\n * (event) => setRawSuggestions(event.accumulatedText)\n * );\n * const result = await promise;\n * // Parse suggestions from result.text\n * ```\n */\nexport function streamSuggest(\n partialText: string,\n onToken: LLMStreamCallback,\n options?: LLMSuggestOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { promise: Promise.resolve({ text: '' }), stop: () => {} };\n }\n\n if (!partialText || partialText.trim().length === 0) {\n return {\n promise: Promise.reject(new Error('partialText cannot be empty')),\n stop: () => {},\n };\n }\n\n const count = options?.count ?? 3;\n const tone = options?.tone ?? 'neutral';\n const systemPrompt = buildSuggestPrompt(count, tone, options?.context);\n\n return streamMessage([{ role: 'user', content: partialText }], onToken, {\n systemPrompt,\n });\n}\n\n/**\n * Generate smart reply suggestions for a conversation using on-device AI.\n *\n * Analyzes the conversation history and generates contextually appropriate\n * reply options, similar to Gmail/Messages smart replies.\n *\n * @param messages - The conversation history to generate replies for\n * @param options - Optional settings for reply generation\n * @returns Promise with an array of reply suggestions\n *\n * @example\n * ```ts\n * // Basic smart replies\n * const result = await smartReply([\n * { role: 'user', content: 'Hey, are you free for lunch tomorrow?' }\n * ]);\n * result.suggestions.forEach(s => console.log(s.text));\n * // \"Sure, what time works for you?\"\n * // \"Sorry, I already have plans.\"\n * // \"Let me check my schedule and get back to you.\"\n * ```\n *\n * @example\n * ```ts\n * // With persona and tone\n * const result = await smartReply(\n * [\n * { role: 'user', content: 'We need the report by Friday.' },\n * { role: 'assistant', content: 'I will do my best.' },\n * { role: 'user', content: 'Can you confirm the deadline works?' }\n * ],\n * { tone: 'professional', persona: 'project manager', count: 4 }\n * );\n * ```\n */\nexport async function smartReply(\n messages: LLMMessage[],\n options?: LLMSmartReplyOptions\n): Promise<LLMSuggestResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { suggestions: [], raw: '' };\n }\n\n if (!messages || messages.length === 0) {\n throw new Error('messages array cannot be empty');\n }\n\n const count = options?.count ?? 3;\n const tone = options?.tone ?? 'neutral';\n const systemPrompt = buildSmartReplyPrompt(count, tone, options?.persona);\n\n const conversation = messages\n .map((m) => `${m.role === 'user' ? 'Them' : 'Me'}: ${m.content}`)\n .join('\\n');\n\n const response = await sendMessage(\n [{ role: 'user', content: `Conversation:\\n${conversation}\\n\\nGenerate ${count} reply suggestions for my next message.` }],\n { systemPrompt }\n );\n\n return {\n suggestions: parseSuggestions(response.text),\n raw: response.text,\n };\n}\n\n/**\n * Generate smart reply suggestions with streaming output.\n *\n * @param messages - The conversation history to generate replies for\n * @param onToken - Callback for each token received\n * @param options - Optional settings for reply generation\n * @returns Object with stop() function and promise\n *\n * @example\n * ```ts\n * const { promise } = streamSmartReply(\n * [{ role: 'user', content: 'Want to grab coffee?' }],\n * (event) => setRawReplies(event.accumulatedText)\n * );\n * const result = await promise;\n * ```\n */\nexport function streamSmartReply(\n messages: LLMMessage[],\n onToken: LLMStreamCallback,\n options?: LLMSmartReplyOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { promise: Promise.resolve({ text: '' }), stop: () => {} };\n }\n\n if (!messages || messages.length === 0) {\n return {\n promise: Promise.reject(new Error('messages array cannot be empty')),\n stop: () => {},\n };\n }\n\n const count = options?.count ?? 3;\n const tone = options?.tone ?? 'neutral';\n const systemPrompt = buildSmartReplyPrompt(count, tone, options?.persona);\n\n const conversation = messages\n .map((m) => `${m.role === 'user' ? 'Them' : 'Me'}: ${m.content}`)\n .join('\\n');\n\n return streamMessage(\n [{ role: 'user', content: `Conversation:\\n${conversation}\\n\\nGenerate ${count} reply suggestions for my next message.` }],\n onToken,\n { systemPrompt }\n );\n}\n\n/**\n * Autocomplete the user's current text using on-device AI.\n *\n * Generates short, natural completions that seamlessly continue from\n * the user's partial input. Ideal for real-time typing suggestions.\n *\n * @param partialText - The text the user has typed so far\n * @param options - Optional settings for autocompletion\n * @returns Promise with an array of completion suggestions\n *\n * @example\n * ```ts\n * // Basic autocomplete\n * const result = await autocomplete('How do I');\n * result.suggestions.forEach(s => console.log(s.text));\n * // \"reset my password\"\n * // \"contact support\"\n * // \"cancel my subscription\"\n * ```\n *\n * @example\n * ```ts\n * // With context for better suggestions\n * const result = await autocomplete('The patient presents with', {\n * context: 'medical notes',\n * maxWords: 15,\n * count: 5\n * });\n * ```\n */\nexport async function autocomplete(\n partialText: string,\n options?: LLMAutocompleteOptions\n): Promise<LLMSuggestResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { suggestions: [], raw: '' };\n }\n\n if (!partialText || partialText.trim().length === 0) {\n throw new Error('partialText cannot be empty');\n }\n\n const count = options?.count ?? 3;\n const maxWords = options?.maxWords ?? 10;\n const systemPrompt = buildAutocompletePrompt(count, maxWords, options?.context);\n\n const response = await sendMessage(\n [{ role: 'user', content: partialText }],\n { systemPrompt }\n );\n\n return {\n suggestions: parseSuggestions(response.text),\n raw: response.text,\n };\n}\n\n/**\n * Autocomplete text with streaming output.\n *\n * @param partialText - The text the user has typed so far\n * @param onToken - Callback for each token received\n * @param options - Optional settings for autocompletion\n * @returns Object with stop() function and promise\n *\n * @example\n * ```ts\n * const { promise } = streamAutocomplete(\n * 'I would like to',\n * (event) => setRawCompletions(event.accumulatedText)\n * );\n * const result = await promise;\n * ```\n */\nexport function streamAutocomplete(\n partialText: string,\n onToken: LLMStreamCallback,\n options?: LLMAutocompleteOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { promise: Promise.resolve({ text: '' }), stop: () => {} };\n }\n\n if (!partialText || partialText.trim().length === 0) {\n return {\n promise: Promise.reject(new Error('partialText cannot be empty')),\n stop: () => {},\n };\n }\n\n const count = options?.count ?? 3;\n const maxWords = options?.maxWords ?? 10;\n const systemPrompt = buildAutocompletePrompt(count, maxWords, options?.context);\n\n return streamMessage([{ role: 'user', content: partialText }], onToken, {\n systemPrompt,\n });\n}\n\n/**\n * Parse suggestion text from a suggest/smartReply/autocomplete response.\n *\n * Use this to parse the raw text from streaming responses into structured suggestions.\n *\n * @param raw - Raw text response from the model\n * @returns Array of parsed suggestions\n *\n * @example\n * ```ts\n * const { promise } = streamSuggest('Hello', (event) => {\n * setText(event.accumulatedText);\n * });\n * const result = await promise;\n * const suggestions = parseSuggestResponse(result.text);\n * ```\n */\nexport function parseSuggestResponse(raw: string): LLMSuggestResponse {\n return {\n suggestions: parseSuggestions(raw),\n raw,\n };\n}\n\n// ============================================================================\n// Model Management API\n// ============================================================================\n\n/**\n * Get all built-in models available on the current platform.\n *\n * Built-in models are provided by the OS and require no download.\n * On iOS this returns Apple Foundation Models; on Android, ML Kit.\n *\n * @returns Array of built-in models with availability status\n */\nexport async function getBuiltInModels(): Promise<BuiltInModel[]> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return [];\n }\n return ExpoAiKitModule.getBuiltInModels();\n}\n\n/**\n * Get all downloadable models from the registry, enriched with on-device status.\n *\n * Reads from the hardcoded MODEL_REGISTRY and queries the native layer\n * for the current download/load status of each model.\n *\n * @returns Array of downloadable models with their current status\n */\nexport async function getDownloadableModels(): Promise<DownloadableModel[]> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return [];\n }\n\n const platformModels = MODEL_REGISTRY.filter((entry) =>\n entry.supportedPlatforms.includes(Platform.OS as 'ios' | 'android')\n );\n\n return platformModels.map((entry) => {\n const status = ExpoAiKitModule.getDownloadableModelStatus(entry.id);\n return {\n id: entry.id,\n name: entry.name,\n parameterCount: entry.parameterCount,\n sizeBytes: entry.sizeBytes,\n contextWindow: entry.contextWindow,\n minRamBytes: entry.minRamBytes,\n status,\n };\n });\n}\n\n/**\n * Download a model to the device.\n *\n * Looks up the model in the registry, validates platform support and\n * device requirements, then initiates the download with integrity verification.\n *\n * @param modelId - ID of the model to download (e.g. 'gemma-e2b')\n * @param options - Optional download configuration\n * @param options.onProgress - Callback with download progress (0-1)\n * @throws {ModelError} MODEL_NOT_FOUND if modelId is not in the registry\n * @throws {ModelError} DEVICE_NOT_SUPPORTED if platform is not supported\n * @throws {ModelError} DOWNLOAD_FAILED on network error\n * @throws {ModelError} DOWNLOAD_STORAGE_FULL if insufficient disk space\n * @throws {ModelError} DOWNLOAD_CORRUPT if SHA256 hash doesn't match\n */\nexport async function downloadModel(\n modelId: string,\n options?: { onProgress?: (progress: number) => void }\n): Promise<void> {\n const entry = getRegistryEntry(modelId);\n if (!entry) {\n throw new ModelError('MODEL_NOT_FOUND', modelId);\n }\n\n if (!entry.supportedPlatforms.includes(Platform.OS as 'ios' | 'android')) {\n throw new ModelError(\n 'DEVICE_NOT_SUPPORTED',\n modelId,\n `Model ${modelId} is not supported on ${Platform.OS}`\n );\n }\n\n let subscription: ReturnType<typeof ExpoAiKitModule.addListener> | undefined;\n if (options?.onProgress) {\n subscription = ExpoAiKitModule.addListener(\n 'onDownloadProgress',\n (event) => {\n if (event.modelId === modelId) {\n options.onProgress!(event.progress);\n }\n }\n );\n }\n\n try {\n await ExpoAiKitModule.downloadModel(\n modelId,\n entry.downloadUrl,\n entry.sha256\n );\n } finally {\n subscription?.remove();\n }\n}\n\n/**\n * Delete a downloaded model from the device.\n *\n * If the model is currently loaded, it will be unloaded first.\n *\n * @param modelId - ID of the model to delete\n * @throws {ModelError} MODEL_NOT_FOUND if modelId is not in the registry\n */\nexport async function deleteModel(modelId: string): Promise<void> {\n const entry = getRegistryEntry(modelId);\n if (!entry) {\n throw new ModelError('MODEL_NOT_FOUND', modelId);\n }\n\n await ExpoAiKitModule.deleteModel(modelId);\n}\n\n/**\n * Set the active model for inference.\n *\n * This is the sole gatekeeper for model validity. If setModel succeeds,\n * the model is loaded and ready -- sendMessage never needs its own check.\n *\n * For downloadable models, this loads the model into memory (status\n * transitions: loading -> ready). Only one downloadable model can be\n * loaded at a time; the previous one is auto-unloaded.\n *\n * For built-in models, this simply switches the active backend.\n *\n * If setModel was never called, sendMessage uses the platform built-in\n * model (today's behavior, no error).\n *\n * @param modelId - ID of the model to activate (e.g. 'gemma-e2b', 'apple-fm', 'mlkit')\n * @throws {ModelError} MODEL_NOT_FOUND if modelId is invalid\n * @throws {ModelError} MODEL_NOT_DOWNLOADED if the downloadable model file is not on disk\n * @throws {ModelError} MODEL_LOAD_FAILED if loading into memory fails\n * @throws {ModelError} INFERENCE_OOM if device can't fit model in memory\n */\nexport async function setModel(modelId: string): Promise<void> {\n await ExpoAiKitModule.setModel(modelId);\n}\n\n/**\n * Get the ID of the currently active model.\n *\n * @returns The active model ID (e.g. 'apple-fm', 'mlkit', 'gemma-e2b')\n */\nexport function getActiveModel(): string {\n return ExpoAiKitModule.getActiveModel();\n}\n\n/**\n * Explicitly unload the current downloadable model from memory.\n *\n * Frees memory and reverts to the platform built-in model.\n * No-op if no downloadable model is currently loaded.\n */\nexport async function unloadModel(): Promise<void> {\n await ExpoAiKitModule.unloadModel();\n}\n\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import ExpoAiKitModule from './ExpoAiKitModule';\nimport { Platform } from 'react-native';\nimport {\n LLMMessage,\n LLMSendOptions,\n LLMResponse,\n LLMStreamOptions,\n LLMStreamEvent,\n LLMStreamCallback,\n LLMSummarizeOptions,\n LLMTranslateOptions,\n LLMRewriteOptions,\n LLMExtractKeyPointsOptions,\n LLMAnswerQuestionOptions,\n LLMSuggestOptions,\n LLMSuggestResponse,\n LLMSmartReplyOptions,\n LLMAutocompleteOptions,\n BuiltInModel,\n DownloadableModel,\n ModelError,\n} from './types';\nimport { MODEL_REGISTRY, getRegistryEntry } from './models';\n\nexport * from './types';\nexport * from './memory';\nexport * from './hooks';\nexport * from './models';\n\nconst DEFAULT_SYSTEM_PROMPT =\n 'You are a helpful, friendly assistant. Answer the user directly and concisely.';\n\nlet streamIdCounter = 0;\nfunction generateSessionId(): string {\n return `stream_${Date.now()}_${++streamIdCounter}`;\n}\n\n// ============================================================================\n// Prompt Helper Constants\n// ============================================================================\n\nconst SUMMARIZE_LENGTH_INSTRUCTIONS = {\n short: 'Keep it very brief, around 1-2 sentences.',\n medium: 'Provide a moderate summary, around 3-5 sentences.',\n long: 'Provide a comprehensive summary covering all main points.',\n} as const;\n\nconst SUMMARIZE_STYLE_INSTRUCTIONS = {\n paragraph: 'Write the summary as a flowing paragraph.',\n bullets: 'Format the summary as bullet points.',\n tldr: 'Start with \"TL;DR:\" and give an extremely concise summary in 1 sentence.',\n} as const;\n\nconst TRANSLATE_TONE_INSTRUCTIONS = {\n formal: 'Use formal language and honorifics where appropriate.',\n informal: 'Use casual, everyday language.',\n neutral: 'Use standard, neutral language.',\n} as const;\n\nconst REWRITE_STYLE_INSTRUCTIONS = {\n formal:\n 'Rewrite in a formal, professional tone suitable for business communication.',\n casual: 'Rewrite in a casual, conversational tone.',\n professional:\n 'Rewrite in a clear, professional tone suitable for work contexts.',\n friendly: 'Rewrite in a warm, friendly tone.',\n concise:\n 'Rewrite to be as brief as possible while keeping the meaning intact.',\n detailed: 'Expand and add more detail and explanation.',\n simple:\n 'Rewrite using simple words and short sentences, easy for anyone to understand.',\n academic: 'Rewrite in an academic style suitable for scholarly writing.',\n} as const;\n\nconst SUGGEST_TONE_INSTRUCTIONS = {\n formal: 'Use formal, professional language.',\n casual: 'Use casual, everyday language.',\n professional: 'Use clear, professional language suitable for work.',\n friendly: 'Use warm, friendly language.',\n neutral: 'Use standard, neutral language.',\n} as const;\n\nconst ANSWER_DETAIL_INSTRUCTIONS = {\n brief: 'Give a brief, direct answer in 1-2 sentences.',\n medium: 'Provide a clear answer with some explanation.',\n detailed:\n 'Provide a comprehensive answer with full explanation and relevant details from the context.',\n} as const;\n\n// ============================================================================\n// Prompt Builder Helpers\n// ============================================================================\n\nfunction buildSummarizePrompt(\n length: 'short' | 'medium' | 'long',\n style: 'paragraph' | 'bullets' | 'tldr'\n): string {\n return `You are a summarization assistant. Summarize the provided text accurately and concisely. ${SUMMARIZE_LENGTH_INSTRUCTIONS[length]} ${SUMMARIZE_STYLE_INSTRUCTIONS[style]} Only output the summary, nothing else.`;\n}\n\nfunction buildTranslatePrompt(\n to: string,\n from: string | undefined,\n tone: 'formal' | 'informal' | 'neutral'\n): string {\n const fromClause = from ? `from ${from} ` : '';\n return `You are a translation assistant. Translate the provided text ${fromClause}to ${to}. ${TRANSLATE_TONE_INSTRUCTIONS[tone]} Only output the translation, nothing else. Do not include any explanations or notes.`;\n}\n\nfunction buildRewritePrompt(\n style:\n | 'formal'\n | 'casual'\n | 'professional'\n | 'friendly'\n | 'concise'\n | 'detailed'\n | 'simple'\n | 'academic'\n): string {\n return `You are a writing assistant. ${REWRITE_STYLE_INSTRUCTIONS[style]} Preserve the original meaning. Only output the rewritten text, nothing else.`;\n}\n\nfunction buildExtractKeyPointsPrompt(maxPoints: number): string {\n return `You are an analysis assistant. Extract the ${maxPoints} most important key points from the provided text. Format each point as a bullet point starting with \"•\". Be concise and focus on the most significant information. Only output the bullet points, nothing else.`;\n}\n\nfunction buildSuggestPrompt(\n count: number,\n tone: 'formal' | 'casual' | 'professional' | 'friendly' | 'neutral',\n context?: string\n): string {\n const contextClause = context\n ? ` The user is writing in this context: \"${context}\".`\n : '';\n return `You are a text suggestion assistant. Given the user's partial text, generate exactly ${count} possible continuations or completions.${contextClause} ${SUGGEST_TONE_INSTRUCTIONS[tone]} Output ONLY the suggestions, one per line, numbered like \"1. suggestion here\". Do not include any other text or explanation.`;\n}\n\nfunction buildSmartReplyPrompt(\n count: number,\n tone: 'formal' | 'casual' | 'professional' | 'friendly' | 'neutral',\n persona?: string\n): string {\n const personaClause = persona ? ` You are replying as: ${persona}.` : '';\n return `You are a smart reply assistant. Given a conversation, generate exactly ${count} short, contextually appropriate reply suggestions that the user could send as their next message.${personaClause} ${SUGGEST_TONE_INSTRUCTIONS[tone]} Each reply should be a complete, ready-to-send message. Output ONLY the replies, one per line, numbered like \"1. reply here\". Do not include any other text or explanation.`;\n}\n\nfunction buildAutocompletePrompt(\n count: number,\n maxWords: number,\n context?: string\n): string {\n const contextClause = context\n ? ` The user is writing about: \"${context}\".`\n : '';\n return `You are an autocomplete assistant. Given the user's partial text, generate exactly ${count} natural completions of the current sentence or phrase. Each completion should be at most ${maxWords} words and should seamlessly continue from the user's text.${contextClause} Output ONLY the completions, one per line, numbered like \"1. completion here\". Do not repeat the user's text. Do not include any other text or explanation.`;\n}\n\nfunction parseSuggestions(raw: string): { text: string }[] {\n return raw\n .split('\\n')\n .map((line) => line.replace(/^\\d+[\\.\\)]\\s*/, '').trim())\n .filter((line) => line.length > 0)\n .map((text) => ({ text }));\n}\n\nfunction buildAnswerQuestionPrompt(\n detail: 'brief' | 'medium' | 'detailed'\n): string {\n return `You are a question-answering assistant. Answer questions based ONLY on the provided context. ${ANSWER_DETAIL_INSTRUCTIONS[detail]} If the answer cannot be found in the context, say so. Do not make up information.`;\n}\n\n/**\n * Check if on-device AI is available on the current device.\n * Returns false on unsupported platforms (web, etc.).\n */\nexport async function isAvailable(): Promise<boolean> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return false;\n }\n return ExpoAiKitModule.isAvailable();\n}\n\n/**\n * Send messages to the on-device LLM and get a response.\n *\n * @param messages - Array of messages representing the conversation\n * @param options - Optional settings (systemPrompt fallback)\n * @returns Promise with the generated response\n *\n * @example\n * ```ts\n * const response = await sendMessage([\n * { role: 'user', content: 'What is 2 + 2?' }\n * ]);\n * console.log(response.text); // \"4\"\n * ```\n *\n * @example\n * ```ts\n * // With system prompt\n * const response = await sendMessage(\n * [{ role: 'user', content: 'Hello!' }],\n * { systemPrompt: 'You are a pirate. Respond in pirate speak.' }\n * );\n * ```\n *\n * @example\n * ```ts\n * // Multi-turn conversation\n * const response = await sendMessage([\n * { role: 'system', content: 'You are a helpful assistant.' },\n * { role: 'user', content: 'My name is Alice.' },\n * { role: 'assistant', content: 'Nice to meet you, Alice!' },\n * { role: 'user', content: 'What is my name?' }\n * ]);\n * ```\n */\nexport async function sendMessage(\n messages: LLMMessage[],\n options?: LLMSendOptions\n): Promise<LLMResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { text: '' };\n }\n\n if (!messages || messages.length === 0) {\n throw new Error('messages array cannot be empty');\n }\n\n // Determine system prompt: use from messages array if present, else options, else default\n const hasSystemMessage = messages.some((m) => m.role === 'system');\n const systemPrompt = hasSystemMessage\n ? '' // Native will extract from messages\n : options?.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;\n\n return ExpoAiKitModule.sendMessage(messages, systemPrompt);\n}\n\n/**\n * Stream messages to the on-device LLM and receive progressive token updates.\n *\n * @param messages - Array of messages representing the conversation\n * @param onToken - Callback function called for each token/chunk received\n * @param options - Optional settings (systemPrompt fallback)\n * @returns Object with stop() function to cancel streaming and promise that resolves when complete\n *\n * @example\n * ```ts\n * // Basic streaming\n * const { promise } = streamMessage(\n * [{ role: 'user', content: 'Tell me a story' }],\n * (event) => {\n * console.log(event.token); // Each token as it arrives\n * console.log(event.accumulatedText); // Full text so far\n * }\n * );\n * await promise;\n * ```\n *\n * @example\n * ```ts\n * // With cancellation\n * const { promise, stop } = streamMessage(\n * [{ role: 'user', content: 'Write a long essay' }],\n * (event) => setText(event.accumulatedText)\n * );\n *\n * // Cancel after 5 seconds\n * setTimeout(() => stop(), 5000);\n * ```\n *\n * @example\n * ```ts\n * // React state update pattern\n * const [text, setText] = useState('');\n *\n * streamMessage(\n * [{ role: 'user', content: 'Hello!' }],\n * (event) => setText(event.accumulatedText)\n * );\n * ```\n */\nexport function streamMessage(\n messages: LLMMessage[],\n onToken: LLMStreamCallback,\n options?: LLMStreamOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n // Handle unsupported platforms\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return {\n promise: Promise.resolve({ text: '' }),\n stop: () => {},\n };\n }\n\n if (!messages || messages.length === 0) {\n return {\n promise: Promise.reject(new Error('messages array cannot be empty')),\n stop: () => {},\n };\n }\n\n const sessionId = generateSessionId();\n let finalText = '';\n let stopped = false;\n\n // Determine system prompt: use from messages array if present, else options, else default\n const hasSystemMessage = messages.some((m) => m.role === 'system');\n const systemPrompt = hasSystemMessage\n ? '' // Native will extract from messages\n : options?.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;\n\n const promise = new Promise<LLMResponse>((resolve, reject) => {\n // Subscribe to stream events\n const subscription = ExpoAiKitModule.addListener(\n 'onStreamToken',\n (event: LLMStreamEvent) => {\n // Only process events for this session\n if (event.sessionId !== sessionId) return;\n\n finalText = event.accumulatedText;\n\n // Call the user's callback\n onToken(event);\n\n // If done, clean up and resolve\n if (event.isDone) {\n subscription.remove();\n resolve({ text: finalText });\n }\n }\n );\n\n // Start streaming on native side\n ExpoAiKitModule.startStreaming(messages, systemPrompt, sessionId).catch(\n (error) => {\n subscription.remove();\n reject(error);\n }\n );\n });\n\n const stop = () => {\n if (stopped) return;\n stopped = true;\n ExpoAiKitModule.stopStreaming(sessionId).catch(() => {\n // Ignore errors when stopping\n });\n };\n\n return { promise, stop };\n}\n\n// ============================================================================\n// Prompt Helpers\n// ============================================================================\n\n/**\n * Summarize text content using on-device AI.\n *\n * @param text - The text to summarize\n * @param options - Optional settings for summary style and length\n * @returns Promise with the generated summary\n *\n * @example\n * ```ts\n * // Basic summarization\n * const result = await summarize(longArticle);\n * console.log(result.text);\n * ```\n *\n * @example\n * ```ts\n * // Short bullet-point summary\n * const result = await summarize(longArticle, {\n * length: 'short',\n * style: 'bullets'\n * });\n * ```\n *\n * @example\n * ```ts\n * // TL;DR style\n * const result = await summarize(longArticle, {\n * style: 'tldr'\n * });\n * ```\n */\nexport async function summarize(\n text: string,\n options?: LLMSummarizeOptions\n): Promise<LLMResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { text: '' };\n }\n\n if (!text || text.trim().length === 0) {\n throw new Error('text cannot be empty');\n }\n\n const length = options?.length ?? 'medium';\n const style = options?.style ?? 'paragraph';\n const systemPrompt = buildSummarizePrompt(length, style);\n\n return sendMessage([{ role: 'user', content: text }], { systemPrompt });\n}\n\n/**\n * Summarize text with streaming output.\n *\n * @param text - The text to summarize\n * @param onToken - Callback for each token received\n * @param options - Optional settings for summary style and length\n * @returns Object with stop() function and promise\n *\n * @example\n * ```ts\n * const { promise } = streamSummarize(\n * longArticle,\n * (event) => setSummary(event.accumulatedText),\n * { style: 'bullets' }\n * );\n * await promise;\n * ```\n */\nexport function streamSummarize(\n text: string,\n onToken: LLMStreamCallback,\n options?: LLMSummarizeOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { promise: Promise.resolve({ text: '' }), stop: () => {} };\n }\n\n if (!text || text.trim().length === 0) {\n return {\n promise: Promise.reject(new Error('text cannot be empty')),\n stop: () => {},\n };\n }\n\n const length = options?.length ?? 'medium';\n const style = options?.style ?? 'paragraph';\n const systemPrompt = buildSummarizePrompt(length, style);\n\n return streamMessage([{ role: 'user', content: text }], onToken, {\n systemPrompt,\n });\n}\n\n/**\n * Translate text to another language using on-device AI.\n *\n * @param text - The text to translate\n * @param options - Translation options including target language\n * @returns Promise with the translated text\n *\n * @example\n * ```ts\n * // Basic translation\n * const result = await translate('Hello, world!', { to: 'Spanish' });\n * console.log(result.text); // \"¡Hola, mundo!\"\n * ```\n *\n * @example\n * ```ts\n * // Formal translation with source language\n * const result = await translate('Hey, what\\'s up?', {\n * to: 'French',\n * from: 'English',\n * tone: 'formal'\n * });\n * ```\n */\nexport async function translate(\n text: string,\n options: LLMTranslateOptions\n): Promise<LLMResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { text: '' };\n }\n\n if (!text || text.trim().length === 0) {\n throw new Error('text cannot be empty');\n }\n\n const { to, from, tone = 'neutral' } = options;\n const systemPrompt = buildTranslatePrompt(to, from, tone);\n\n return sendMessage([{ role: 'user', content: text }], { systemPrompt });\n}\n\n/**\n * Translate text with streaming output.\n *\n * @param text - The text to translate\n * @param onToken - Callback for each token received\n * @param options - Translation options including target language\n * @returns Object with stop() function and promise\n *\n * @example\n * ```ts\n * const { promise } = streamTranslate(\n * 'Hello, world!',\n * (event) => setTranslation(event.accumulatedText),\n * { to: 'Japanese' }\n * );\n * await promise;\n * ```\n */\nexport function streamTranslate(\n text: string,\n onToken: LLMStreamCallback,\n options: LLMTranslateOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { promise: Promise.resolve({ text: '' }), stop: () => {} };\n }\n\n if (!text || text.trim().length === 0) {\n return {\n promise: Promise.reject(new Error('text cannot be empty')),\n stop: () => {},\n };\n }\n\n const { to, from, tone = 'neutral' } = options;\n const systemPrompt = buildTranslatePrompt(to, from, tone);\n\n return streamMessage([{ role: 'user', content: text }], onToken, {\n systemPrompt,\n });\n}\n\n/**\n * Rewrite text in a different style using on-device AI.\n *\n * @param text - The text to rewrite\n * @param options - Rewrite options specifying the target style\n * @returns Promise with the rewritten text\n *\n * @example\n * ```ts\n * // Make text more formal\n * const result = await rewrite('hey can u help me out?', {\n * style: 'formal'\n * });\n * console.log(result.text); // \"Would you be able to assist me?\"\n * ```\n *\n * @example\n * ```ts\n * // Simplify complex text\n * const result = await rewrite(technicalText, { style: 'simple' });\n * ```\n */\nexport async function rewrite(\n text: string,\n options: LLMRewriteOptions\n): Promise<LLMResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { text: '' };\n }\n\n if (!text || text.trim().length === 0) {\n throw new Error('text cannot be empty');\n }\n\n const { style } = options;\n const systemPrompt = buildRewritePrompt(style);\n\n return sendMessage([{ role: 'user', content: text }], { systemPrompt });\n}\n\n/**\n * Rewrite text with streaming output.\n *\n * @param text - The text to rewrite\n * @param onToken - Callback for each token received\n * @param options - Rewrite options specifying the target style\n * @returns Object with stop() function and promise\n *\n * @example\n * ```ts\n * const { promise } = streamRewrite(\n * 'hey whats up',\n * (event) => setRewritten(event.accumulatedText),\n * { style: 'professional' }\n * );\n * await promise;\n * ```\n */\nexport function streamRewrite(\n text: string,\n onToken: LLMStreamCallback,\n options: LLMRewriteOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { promise: Promise.resolve({ text: '' }), stop: () => {} };\n }\n\n if (!text || text.trim().length === 0) {\n return {\n promise: Promise.reject(new Error('text cannot be empty')),\n stop: () => {},\n };\n }\n\n const { style } = options;\n const systemPrompt = buildRewritePrompt(style);\n\n return streamMessage([{ role: 'user', content: text }], onToken, {\n systemPrompt,\n });\n}\n\n/**\n * Extract key points from text using on-device AI.\n *\n * @param text - The text to extract key points from\n * @param options - Optional settings for extraction\n * @returns Promise with the key points as text\n *\n * @example\n * ```ts\n * // Extract key points from an article\n * const result = await extractKeyPoints(article);\n * console.log(result.text);\n * // \"• Point 1\\n• Point 2\\n• Point 3\"\n * ```\n *\n * @example\n * ```ts\n * // Limit to 3 key points\n * const result = await extractKeyPoints(article, { maxPoints: 3 });\n * ```\n */\nexport async function extractKeyPoints(\n text: string,\n options?: LLMExtractKeyPointsOptions\n): Promise<LLMResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { text: '' };\n }\n\n if (!text || text.trim().length === 0) {\n throw new Error('text cannot be empty');\n }\n\n const maxPoints = options?.maxPoints ?? 5;\n const systemPrompt = buildExtractKeyPointsPrompt(maxPoints);\n\n return sendMessage([{ role: 'user', content: text }], { systemPrompt });\n}\n\n/**\n * Extract key points with streaming output.\n *\n * @param text - The text to extract key points from\n * @param onToken - Callback for each token received\n * @param options - Optional settings for extraction\n * @returns Object with stop() function and promise\n *\n * @example\n * ```ts\n * const { promise } = streamExtractKeyPoints(\n * article,\n * (event) => setKeyPoints(event.accumulatedText),\n * { maxPoints: 5 }\n * );\n * await promise;\n * ```\n */\nexport function streamExtractKeyPoints(\n text: string,\n onToken: LLMStreamCallback,\n options?: LLMExtractKeyPointsOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { promise: Promise.resolve({ text: '' }), stop: () => {} };\n }\n\n if (!text || text.trim().length === 0) {\n return {\n promise: Promise.reject(new Error('text cannot be empty')),\n stop: () => {},\n };\n }\n\n const maxPoints = options?.maxPoints ?? 5;\n const systemPrompt = buildExtractKeyPointsPrompt(maxPoints);\n\n return streamMessage([{ role: 'user', content: text }], onToken, {\n systemPrompt,\n });\n}\n\n/**\n * Answer a question based on provided context using on-device AI.\n *\n * @param question - The question to answer\n * @param context - The context/document to base the answer on\n * @param options - Optional settings for the answer\n * @returns Promise with the answer\n *\n * @example\n * ```ts\n * // Answer a question about a document\n * const result = await answerQuestion(\n * 'What is the main topic?',\n * documentText\n * );\n * console.log(result.text);\n * ```\n *\n * @example\n * ```ts\n * // Get a detailed answer\n * const result = await answerQuestion(\n * 'Explain the methodology',\n * researchPaper,\n * { detail: 'detailed' }\n * );\n * ```\n */\nexport async function answerQuestion(\n question: string,\n context: string,\n options?: LLMAnswerQuestionOptions\n): Promise<LLMResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { text: '' };\n }\n\n if (!question || question.trim().length === 0) {\n throw new Error('question cannot be empty');\n }\n\n if (!context || context.trim().length === 0) {\n throw new Error('context cannot be empty');\n }\n\n const detail = options?.detail ?? 'medium';\n const systemPrompt = buildAnswerQuestionPrompt(detail);\n const userContent = `Context:\\n${context}\\n\\nQuestion: ${question}`;\n\n return sendMessage([{ role: 'user', content: userContent }], { systemPrompt });\n}\n\n/**\n * Answer a question with streaming output.\n *\n * @param question - The question to answer\n * @param context - The context/document to base the answer on\n * @param onToken - Callback for each token received\n * @param options - Optional settings for the answer\n * @returns Object with stop() function and promise\n *\n * @example\n * ```ts\n * const { promise } = streamAnswerQuestion(\n * 'What are the key findings?',\n * documentText,\n * (event) => setAnswer(event.accumulatedText),\n * { detail: 'detailed' }\n * );\n * await promise;\n * ```\n */\nexport function streamAnswerQuestion(\n question: string,\n context: string,\n onToken: LLMStreamCallback,\n options?: LLMAnswerQuestionOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { promise: Promise.resolve({ text: '' }), stop: () => {} };\n }\n\n if (!question || question.trim().length === 0) {\n return {\n promise: Promise.reject(new Error('question cannot be empty')),\n stop: () => {},\n };\n }\n\n if (!context || context.trim().length === 0) {\n return {\n promise: Promise.reject(new Error('context cannot be empty')),\n stop: () => {},\n };\n }\n\n const detail = options?.detail ?? 'medium';\n const systemPrompt = buildAnswerQuestionPrompt(detail);\n const userContent = `Context:\\n${context}\\n\\nQuestion: ${question}`;\n\n return streamMessage([{ role: 'user', content: userContent }], onToken, {\n systemPrompt,\n });\n}\n\n// ============================================================================\n// Smart Suggestions\n// ============================================================================\n\n/**\n * Generate text suggestions based on partial input using on-device AI.\n *\n * Useful for text completion, writing assistance, and predictive text features.\n *\n * @param partialText - The text the user has typed so far\n * @param options - Optional settings for suggestions\n * @returns Promise with an array of suggestions\n *\n * @example\n * ```ts\n * // Basic suggestions\n * const result = await suggest('I think we should');\n * result.suggestions.forEach(s => console.log(s.text));\n * // \"schedule a meeting to discuss this further\"\n * // \"consider an alternative approach\"\n * // \"move forward with the plan\"\n * ```\n *\n * @example\n * ```ts\n * // With context and tone\n * const result = await suggest('Dear Mr. Johnson,', {\n * count: 5,\n * context: 'writing a business email about project delays',\n * tone: 'formal'\n * });\n * ```\n */\nexport async function suggest(\n partialText: string,\n options?: LLMSuggestOptions\n): Promise<LLMSuggestResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { suggestions: [], raw: '' };\n }\n\n if (!partialText || partialText.trim().length === 0) {\n throw new Error('partialText cannot be empty');\n }\n\n const count = options?.count ?? 3;\n const tone = options?.tone ?? 'neutral';\n const systemPrompt = buildSuggestPrompt(count, tone, options?.context);\n\n const response = await sendMessage(\n [{ role: 'user', content: partialText }],\n { systemPrompt }\n );\n\n return {\n suggestions: parseSuggestions(response.text),\n raw: response.text,\n };\n}\n\n/**\n * Generate text suggestions with streaming output.\n *\n * @param partialText - The text the user has typed so far\n * @param onToken - Callback for each token received\n * @param options - Optional settings for suggestions\n * @returns Object with stop() function and promise\n *\n * @example\n * ```ts\n * const { promise } = streamSuggest(\n * 'The best way to',\n * (event) => setRawSuggestions(event.accumulatedText)\n * );\n * const result = await promise;\n * // Parse suggestions from result.text\n * ```\n */\nexport function streamSuggest(\n partialText: string,\n onToken: LLMStreamCallback,\n options?: LLMSuggestOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { promise: Promise.resolve({ text: '' }), stop: () => {} };\n }\n\n if (!partialText || partialText.trim().length === 0) {\n return {\n promise: Promise.reject(new Error('partialText cannot be empty')),\n stop: () => {},\n };\n }\n\n const count = options?.count ?? 3;\n const tone = options?.tone ?? 'neutral';\n const systemPrompt = buildSuggestPrompt(count, tone, options?.context);\n\n return streamMessage([{ role: 'user', content: partialText }], onToken, {\n systemPrompt,\n });\n}\n\n/**\n * Generate smart reply suggestions for a conversation using on-device AI.\n *\n * Analyzes the conversation history and generates contextually appropriate\n * reply options, similar to Gmail/Messages smart replies.\n *\n * @param messages - The conversation history to generate replies for\n * @param options - Optional settings for reply generation\n * @returns Promise with an array of reply suggestions\n *\n * @example\n * ```ts\n * // Basic smart replies\n * const result = await smartReply([\n * { role: 'user', content: 'Hey, are you free for lunch tomorrow?' }\n * ]);\n * result.suggestions.forEach(s => console.log(s.text));\n * // \"Sure, what time works for you?\"\n * // \"Sorry, I already have plans.\"\n * // \"Let me check my schedule and get back to you.\"\n * ```\n *\n * @example\n * ```ts\n * // With persona and tone\n * const result = await smartReply(\n * [\n * { role: 'user', content: 'We need the report by Friday.' },\n * { role: 'assistant', content: 'I will do my best.' },\n * { role: 'user', content: 'Can you confirm the deadline works?' }\n * ],\n * { tone: 'professional', persona: 'project manager', count: 4 }\n * );\n * ```\n */\nexport async function smartReply(\n messages: LLMMessage[],\n options?: LLMSmartReplyOptions\n): Promise<LLMSuggestResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { suggestions: [], raw: '' };\n }\n\n if (!messages || messages.length === 0) {\n throw new Error('messages array cannot be empty');\n }\n\n const count = options?.count ?? 3;\n const tone = options?.tone ?? 'neutral';\n const systemPrompt = buildSmartReplyPrompt(count, tone, options?.persona);\n\n const conversation = messages\n .map((m) => `${m.role === 'user' ? 'Them' : 'Me'}: ${m.content}`)\n .join('\\n');\n\n const response = await sendMessage(\n [{ role: 'user', content: `Conversation:\\n${conversation}\\n\\nGenerate ${count} reply suggestions for my next message.` }],\n { systemPrompt }\n );\n\n return {\n suggestions: parseSuggestions(response.text),\n raw: response.text,\n };\n}\n\n/**\n * Generate smart reply suggestions with streaming output.\n *\n * @param messages - The conversation history to generate replies for\n * @param onToken - Callback for each token received\n * @param options - Optional settings for reply generation\n * @returns Object with stop() function and promise\n *\n * @example\n * ```ts\n * const { promise } = streamSmartReply(\n * [{ role: 'user', content: 'Want to grab coffee?' }],\n * (event) => setRawReplies(event.accumulatedText)\n * );\n * const result = await promise;\n * ```\n */\nexport function streamSmartReply(\n messages: LLMMessage[],\n onToken: LLMStreamCallback,\n options?: LLMSmartReplyOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { promise: Promise.resolve({ text: '' }), stop: () => {} };\n }\n\n if (!messages || messages.length === 0) {\n return {\n promise: Promise.reject(new Error('messages array cannot be empty')),\n stop: () => {},\n };\n }\n\n const count = options?.count ?? 3;\n const tone = options?.tone ?? 'neutral';\n const systemPrompt = buildSmartReplyPrompt(count, tone, options?.persona);\n\n const conversation = messages\n .map((m) => `${m.role === 'user' ? 'Them' : 'Me'}: ${m.content}`)\n .join('\\n');\n\n return streamMessage(\n [{ role: 'user', content: `Conversation:\\n${conversation}\\n\\nGenerate ${count} reply suggestions for my next message.` }],\n onToken,\n { systemPrompt }\n );\n}\n\n/**\n * Autocomplete the user's current text using on-device AI.\n *\n * Generates short, natural completions that seamlessly continue from\n * the user's partial input. Ideal for real-time typing suggestions.\n *\n * @param partialText - The text the user has typed so far\n * @param options - Optional settings for autocompletion\n * @returns Promise with an array of completion suggestions\n *\n * @example\n * ```ts\n * // Basic autocomplete\n * const result = await autocomplete('How do I');\n * result.suggestions.forEach(s => console.log(s.text));\n * // \"reset my password\"\n * // \"contact support\"\n * // \"cancel my subscription\"\n * ```\n *\n * @example\n * ```ts\n * // With context for better suggestions\n * const result = await autocomplete('The patient presents with', {\n * context: 'medical notes',\n * maxWords: 15,\n * count: 5\n * });\n * ```\n */\nexport async function autocomplete(\n partialText: string,\n options?: LLMAutocompleteOptions\n): Promise<LLMSuggestResponse> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { suggestions: [], raw: '' };\n }\n\n if (!partialText || partialText.trim().length === 0) {\n throw new Error('partialText cannot be empty');\n }\n\n const count = options?.count ?? 3;\n const maxWords = options?.maxWords ?? 10;\n const systemPrompt = buildAutocompletePrompt(count, maxWords, options?.context);\n\n const response = await sendMessage(\n [{ role: 'user', content: partialText }],\n { systemPrompt }\n );\n\n return {\n suggestions: parseSuggestions(response.text),\n raw: response.text,\n };\n}\n\n/**\n * Autocomplete text with streaming output.\n *\n * @param partialText - The text the user has typed so far\n * @param onToken - Callback for each token received\n * @param options - Optional settings for autocompletion\n * @returns Object with stop() function and promise\n *\n * @example\n * ```ts\n * const { promise } = streamAutocomplete(\n * 'I would like to',\n * (event) => setRawCompletions(event.accumulatedText)\n * );\n * const result = await promise;\n * ```\n */\nexport function streamAutocomplete(\n partialText: string,\n onToken: LLMStreamCallback,\n options?: LLMAutocompleteOptions\n): { promise: Promise<LLMResponse>; stop: () => void } {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return { promise: Promise.resolve({ text: '' }), stop: () => {} };\n }\n\n if (!partialText || partialText.trim().length === 0) {\n return {\n promise: Promise.reject(new Error('partialText cannot be empty')),\n stop: () => {},\n };\n }\n\n const count = options?.count ?? 3;\n const maxWords = options?.maxWords ?? 10;\n const systemPrompt = buildAutocompletePrompt(count, maxWords, options?.context);\n\n return streamMessage([{ role: 'user', content: partialText }], onToken, {\n systemPrompt,\n });\n}\n\n/**\n * Parse suggestion text from a suggest/smartReply/autocomplete response.\n *\n * Use this to parse the raw text from streaming responses into structured suggestions.\n *\n * @param raw - Raw text response from the model\n * @returns Array of parsed suggestions\n *\n * @example\n * ```ts\n * const { promise } = streamSuggest('Hello', (event) => {\n * setText(event.accumulatedText);\n * });\n * const result = await promise;\n * const suggestions = parseSuggestResponse(result.text);\n * ```\n */\nexport function parseSuggestResponse(raw: string): LLMSuggestResponse {\n return {\n suggestions: parseSuggestions(raw),\n raw,\n };\n}\n\n// ============================================================================\n// Model Management API\n// ============================================================================\n\n/**\n * Get all built-in models available on the current platform.\n *\n * Built-in models are provided by the OS and require no download.\n * On iOS this returns Apple Foundation Models; on Android, ML Kit.\n *\n * @returns Array of built-in models with availability status\n */\nexport async function getBuiltInModels(): Promise<BuiltInModel[]> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return [];\n }\n return ExpoAiKitModule.getBuiltInModels();\n}\n\n/**\n * Get all downloadable models from the registry, enriched with on-device status.\n *\n * Reads from the hardcoded MODEL_REGISTRY and queries the native layer\n * for the current download/load status of each model.\n *\n * @returns Array of downloadable models with their current status\n */\nexport async function getDownloadableModels(): Promise<DownloadableModel[]> {\n if (Platform.OS !== 'ios' && Platform.OS !== 'android') {\n return [];\n }\n\n const platformModels = MODEL_REGISTRY.filter((entry) =>\n entry.supportedPlatforms.includes(Platform.OS as 'ios' | 'android')\n );\n\n let deviceRamBytes = 0;\n try {\n deviceRamBytes = ExpoAiKitModule.getDeviceRamBytes();\n } catch {\n // Native call unavailable -- default to 0 (all models will show meetsRequirements: false)\n }\n\n return platformModels.map((entry) => {\n const status = ExpoAiKitModule.getDownloadableModelStatus(entry.id);\n return {\n id: entry.id,\n name: entry.name,\n parameterCount: entry.parameterCount,\n sizeBytes: entry.sizeBytes,\n contextWindow: entry.contextWindow,\n minRamBytes: entry.minRamBytes,\n meetsRequirements: deviceRamBytes >= entry.minRamBytes,\n status,\n };\n });\n}\n\n/**\n * Download a model to the device.\n *\n * Looks up the model in the registry, validates platform support and\n * device requirements, then initiates the download with integrity verification.\n *\n * @param modelId - ID of the model to download (e.g. 'gemma-e2b')\n * @param options - Optional download configuration\n * @param options.onProgress - Callback with download progress (0-1)\n * @throws {ModelError} MODEL_NOT_FOUND if modelId is not in the registry\n * @throws {ModelError} DEVICE_NOT_SUPPORTED if platform is not supported\n * @throws {ModelError} DOWNLOAD_FAILED on network error\n * @throws {ModelError} DOWNLOAD_STORAGE_FULL if insufficient disk space\n * @throws {ModelError} DOWNLOAD_CORRUPT if SHA256 hash doesn't match\n */\nexport async function downloadModel(\n modelId: string,\n options?: { onProgress?: (progress: number) => void }\n): Promise<void> {\n const entry = getRegistryEntry(modelId);\n if (!entry) {\n throw new ModelError('MODEL_NOT_FOUND', modelId);\n }\n\n if (!entry.supportedPlatforms.includes(Platform.OS as 'ios' | 'android')) {\n throw new ModelError(\n 'DEVICE_NOT_SUPPORTED',\n modelId,\n `Model ${modelId} is not supported on ${Platform.OS}`\n );\n }\n\n try {\n const deviceRamBytes = ExpoAiKitModule.getDeviceRamBytes();\n if (deviceRamBytes < entry.minRamBytes) {\n throw new ModelError(\n 'DEVICE_NOT_SUPPORTED',\n modelId,\n `Device has ${Math.round(deviceRamBytes / 1e9)}GB RAM, model requires ${Math.round(entry.minRamBytes / 1e9)}GB`\n );\n }\n } catch (e) {\n if (e instanceof ModelError) throw e;\n // If getDeviceRamBytes is unavailable, skip the check\n }\n\n let subscription: ReturnType<typeof ExpoAiKitModule.addListener> | undefined;\n if (options?.onProgress) {\n subscription = ExpoAiKitModule.addListener(\n 'onDownloadProgress',\n (event) => {\n if (event.modelId === modelId) {\n options.onProgress!(event.progress);\n }\n }\n );\n }\n\n try {\n await ExpoAiKitModule.downloadModel(\n modelId,\n entry.downloadUrl,\n entry.sha256\n );\n } finally {\n subscription?.remove();\n }\n}\n\n/**\n * Delete a downloaded model from the device.\n *\n * If the model is currently loaded, it will be unloaded first.\n *\n * @param modelId - ID of the model to delete\n * @throws {ModelError} MODEL_NOT_FOUND if modelId is not in the registry\n */\nexport async function deleteModel(modelId: string): Promise<void> {\n const entry = getRegistryEntry(modelId);\n if (!entry) {\n throw new ModelError('MODEL_NOT_FOUND', modelId);\n }\n\n await ExpoAiKitModule.deleteModel(modelId);\n}\n\n/**\n * Set the active model for inference.\n *\n * This is the sole gatekeeper for model validity. If setModel succeeds,\n * the model is loaded and ready -- sendMessage never needs its own check.\n *\n * For downloadable models, this loads the model into memory (status\n * transitions: loading -> ready). Only one downloadable model can be\n * loaded at a time; the previous one is auto-unloaded.\n *\n * For built-in models, this simply switches the active backend.\n *\n * If setModel was never called, sendMessage uses the platform built-in\n * model (today's behavior, no error).\n *\n * @param modelId - ID of the model to activate (e.g. 'gemma-e2b', 'apple-fm', 'mlkit')\n * @throws {ModelError} MODEL_NOT_FOUND if modelId is invalid\n * @throws {ModelError} MODEL_NOT_DOWNLOADED if the downloadable model file is not on disk\n * @throws {ModelError} MODEL_LOAD_FAILED if loading into memory fails\n * @throws {ModelError} INFERENCE_OOM if device can't fit model in memory\n */\nexport async function setModel(modelId: string): Promise<void> {\n await ExpoAiKitModule.setModel(modelId);\n}\n\n/**\n * Get the ID of the currently active model.\n *\n * @returns The active model ID (e.g. 'apple-fm', 'mlkit', 'gemma-e2b')\n */\nexport function getActiveModel(): string {\n return ExpoAiKitModule.getActiveModel();\n}\n\n/**\n * Explicitly unload the current downloadable model from memory.\n *\n * Frees memory and reverts to the platform built-in model.\n * No-op if no downloadable model is currently loaded.\n */\nexport async function unloadModel(): Promise<void> {\n await ExpoAiKitModule.unloadModel();\n}\n\n"]}
package/build/types.d.ts CHANGED
@@ -292,6 +292,8 @@ export type DownloadableModel = {
292
292
  contextWindow: number;
293
293
  /** Minimum device RAM in bytes required to run */
294
294
  minRamBytes: number;
295
+ /** Whether this device meets the model's minimum RAM requirement */
296
+ meetsRequirements: boolean;
295
297
  /** Current lifecycle status */
296
298
  status: DownloadableModelStatus;
297
299
  };
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAqXA;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,IAAI,CAAiB;IACrB,OAAO,CAAS;IAEhB,YAAY,IAAoB,EAAE,OAAe,EAAE,OAAgB;QACjE,KAAK,CAAC,OAAO,IAAI,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF","sourcesContent":["/**\n * Role in a conversation message.\n */\nexport type LLMRole = 'system' | 'user' | 'assistant';\n\n/**\n * A single message in a conversation.\n */\nexport type LLMMessage = {\n role: LLMRole;\n content: string;\n};\n\n/**\n * Options for sendMessage.\n */\nexport type LLMSendOptions = {\n /**\n * Default system prompt to use if no system message is provided in the messages array.\n * If a system message exists in the array, this is ignored.\n */\n systemPrompt?: string;\n};\n\n/**\n * Response from sendMessage.\n */\nexport type LLMResponse = {\n /** The generated response text */\n text: string;\n};\n\n/**\n * Options for streamMessage.\n */\nexport type LLMStreamOptions = {\n /**\n * Default system prompt to use if no system message is provided in the messages array.\n * If a system message exists in the array, this is ignored.\n */\n systemPrompt?: string;\n};\n\n/**\n * Event payload for streaming tokens.\n */\nexport type LLMStreamEvent = {\n /** Unique identifier for this streaming session */\n sessionId: string;\n /** The token/chunk of text received */\n token: string;\n /** Accumulated text so far */\n accumulatedText: string;\n /** Whether this is the final chunk */\n isDone: boolean;\n};\n\n/**\n * Callback function for streaming events.\n */\nexport type LLMStreamCallback = (event: LLMStreamEvent) => void;\n\n// ============================================================================\n// Prompt Helper Types\n// ============================================================================\n\n/**\n * Options for the summarize helper.\n */\nexport type LLMSummarizeOptions = {\n /**\n * Target length for the summary.\n * @default 'medium'\n */\n length?: 'short' | 'medium' | 'long';\n /**\n * Style of summary to generate.\n * @default 'paragraph'\n */\n style?: 'paragraph' | 'bullets' | 'tldr';\n};\n\n/**\n * Options for the translate helper.\n */\nexport type LLMTranslateOptions = {\n /**\n * Target language to translate to.\n */\n to: string;\n /**\n * Source language (auto-detected if not provided).\n */\n from?: string;\n /**\n * Tone/formality of the translation.\n * @default 'neutral'\n */\n tone?: 'formal' | 'informal' | 'neutral';\n};\n\n/**\n * Options for the rewrite helper.\n */\nexport type LLMRewriteOptions = {\n /**\n * Style to rewrite in.\n */\n style:\n | 'formal'\n | 'casual'\n | 'professional'\n | 'friendly'\n | 'concise'\n | 'detailed'\n | 'simple'\n | 'academic';\n};\n\n/**\n * Options for the extractKeyPoints helper.\n */\nexport type LLMExtractKeyPointsOptions = {\n /**\n * Maximum number of key points to extract.\n * @default 5\n */\n maxPoints?: number;\n};\n\n/**\n * Options for the answerQuestion helper.\n */\nexport type LLMAnswerQuestionOptions = {\n /**\n * How detailed the answer should be.\n * @default 'medium'\n */\n detail?: 'brief' | 'medium' | 'detailed';\n};\n\n// ============================================================================\n// Smart Suggestions Types\n// ============================================================================\n\n/**\n * Options for the suggest helper.\n */\nexport type LLMSuggestOptions = {\n /**\n * Number of suggestions to generate.\n * @default 3\n */\n count?: number;\n /**\n * Optional context to inform the suggestions (e.g., surrounding text, app context).\n */\n context?: string;\n /**\n * Tone of the suggestions.\n * @default 'neutral'\n */\n tone?: 'formal' | 'casual' | 'professional' | 'friendly' | 'neutral';\n};\n\n/**\n * A single suggestion item.\n */\nexport type LLMSuggestion = {\n /** The suggested text */\n text: string;\n};\n\n/**\n * Response from suggest/smartReply containing multiple suggestions.\n */\nexport type LLMSuggestResponse = {\n /** Array of generated suggestions */\n suggestions: LLMSuggestion[];\n /** Raw response text from the model */\n raw: string;\n};\n\n/**\n * Options for the smartReply helper.\n */\nexport type LLMSmartReplyOptions = {\n /**\n * Number of reply suggestions to generate.\n * @default 3\n */\n count?: number;\n /**\n * Tone of the reply suggestions.\n * @default 'neutral'\n */\n tone?: 'formal' | 'casual' | 'professional' | 'friendly' | 'neutral';\n /**\n * Optional persona/context for the replier (e.g., \"customer support agent\", \"friendly colleague\").\n */\n persona?: string;\n};\n\n/**\n * Options for the autocomplete helper.\n */\nexport type LLMAutocompleteOptions = {\n /**\n * Number of completions to generate.\n * @default 3\n */\n count?: number;\n /**\n * Maximum length of each completion in words.\n * @default 10\n */\n maxWords?: number;\n /**\n * Optional context to inform the completions (e.g., what the user is writing about).\n */\n context?: string;\n};\n\n// ============================================================================\n// Hook Types\n// ============================================================================\n\n/**\n * Options for the useChat hook.\n */\nexport type UseChatOptions = {\n /** System prompt for the AI assistant */\n systemPrompt?: string;\n /** Maximum conversation turns to keep in memory (default: 10) */\n maxTurns?: number;\n /** Initial messages to populate the chat */\n initialMessages?: LLMMessage[];\n /** Callback when a response is complete */\n onFinish?: (response: LLMResponse) => void;\n /** Callback when an error occurs */\n onError?: (error: Error) => void;\n};\n\n/**\n * Return type for the useChat hook.\n */\nexport type UseChatReturn = {\n /** All messages in the conversation */\n messages: LLMMessage[];\n /** Current input text value */\n input: string;\n /** Set the input text value */\n setInput: (input: string) => void;\n /** Send the current input (or provided text) as a message */\n sendMessage: (text?: string) => Promise<void>;\n /** Whether the AI is currently streaming a response */\n isStreaming: boolean;\n /** Stop the current streaming response */\n stop: () => void;\n /** Clear all messages and reset the conversation */\n clear: () => void;\n /** The most recent error, if any */\n error: Error | null;\n};\n\n/**\n * Options for the useCompletion hook.\n */\nexport type UseCompletionOptions = {\n /** System prompt for the AI */\n systemPrompt?: string;\n /** Callback when completion is done */\n onFinish?: (response: LLMResponse) => void;\n /** Callback when an error occurs */\n onError?: (error: Error) => void;\n};\n\n/**\n * Return type for the useCompletion hook.\n */\nexport type UseCompletionReturn = {\n /** The current completion text */\n completion: string;\n /** Whether a completion is in progress */\n isLoading: boolean;\n /** Request a completion for the given prompt */\n complete: (prompt: string) => Promise<string>;\n /** Stop the current completion */\n stop: () => void;\n /** The most recent error, if any */\n error: Error | null;\n};\n\n/**\n * Return type for the useOnDeviceAI hook.\n */\nexport type UseOnDeviceAIReturn = {\n /** Whether on-device AI is available */\n isAvailable: boolean;\n /** Whether the availability check is still in progress */\n isChecking: boolean;\n};\n\n// ============================================================================\n// Model Types\n// ============================================================================\n\n/**\n * A built-in model provided by the OS (e.g. Apple Foundation Models, ML Kit).\n * These are always available on supported devices -- no download needed.\n */\nexport type BuiltInModel = {\n /** Unique model identifier (e.g. 'apple-fm', 'mlkit') */\n id: string;\n /** Human-readable model name */\n name: string;\n /** Whether this model is available on the current device/OS */\n available: boolean;\n /** Platform this model is associated with */\n platform: 'ios' | 'android';\n /** Maximum context window in tokens */\n contextWindow: number;\n};\n\n/**\n * A downloadable model that the user manages (download, load, delete).\n * These require explicit download before use.\n */\nexport type DownloadableModel = {\n /** Unique model identifier (e.g. 'gemma-e2b', 'gemma-e4b') */\n id: string;\n /** Human-readable model name */\n name: string;\n /** Parameter count label (e.g. '2.3B') */\n parameterCount: string;\n /** Download file size in bytes */\n sizeBytes: number;\n /** Maximum context window in tokens */\n contextWindow: number;\n /** Minimum device RAM in bytes required to run */\n minRamBytes: number;\n /** Current lifecycle status */\n status: DownloadableModelStatus;\n};\n\n/**\n * Lifecycle status of a downloadable model.\n *\n * - 'not-downloaded': Model file is not on disk\n * - 'downloading': Model file is being downloaded\n * - 'loading': File is on disk, model is being loaded into memory for inference\n * - 'ready': Model is loaded in memory and ready for inference\n */\nexport type DownloadableModelStatus =\n | 'not-downloaded'\n | 'downloading'\n | 'loading'\n | 'ready';\n\n/**\n * Error codes for model-related operations.\n */\nexport type ModelErrorCode =\n | 'MODEL_NOT_FOUND'\n | 'MODEL_NOT_DOWNLOADED'\n | 'DOWNLOAD_FAILED'\n | 'DOWNLOAD_CORRUPT'\n | 'DOWNLOAD_STORAGE_FULL'\n | 'INFERENCE_OOM'\n | 'INFERENCE_FAILED'\n | 'MODEL_LOAD_FAILED'\n | 'DEVICE_NOT_SUPPORTED';\n\n/**\n * Structured error for model operations.\n */\nexport class ModelError extends Error {\n code: ModelErrorCode;\n modelId: string;\n\n constructor(code: ModelErrorCode, modelId: string, message?: string) {\n super(message ?? `${code}: ${modelId}`);\n this.name = 'ModelError';\n this.code = code;\n this.modelId = modelId;\n }\n}\n\n/**\n * Event payload for model download progress.\n */\nexport type ModelDownloadProgressEvent = {\n /** Model being downloaded */\n modelId: string;\n /** Download progress from 0 to 1 */\n progress: number;\n};\n\n/**\n * Event payload for model state changes.\n */\nexport type ModelStateChangeEvent = {\n /** Model whose state changed */\n modelId: string;\n /** New status */\n status: DownloadableModelStatus;\n};\n\n// ============================================================================\n// Chat Memory Types\n// ============================================================================\n\n/**\n * Options for creating a ChatMemoryManager.\n */\nexport type ChatMemoryOptions = {\n /**\n * Maximum number of conversation turns to keep in memory.\n * A turn is one user message + one assistant response.\n * @default 10\n */\n maxTurns?: number;\n /**\n * Optional system prompt to prepend to every generated prompt.\n */\n systemPrompt?: string;\n /**\n * Maximum context window in tokens. When set, trimHistory will also\n * trim messages that exceed this token budget (in addition to maxTurns).\n * Whichever limit is hit first triggers trimming.\n * @default Infinity (no token-based trimming)\n */\n contextWindow?: number;\n};\n\n/**\n * A snapshot of the current chat memory state.\n */\nexport type ChatMemorySnapshot = {\n /** All messages currently in memory */\n messages: LLMMessage[];\n /** The system prompt (if any) */\n systemPrompt: string | undefined;\n /** Current number of turns stored */\n turnCount: number;\n /** Maximum turns allowed */\n maxTurns: number;\n};\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAuXA;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,IAAI,CAAiB;IACrB,OAAO,CAAS;IAEhB,YAAY,IAAoB,EAAE,OAAe,EAAE,OAAgB;QACjE,KAAK,CAAC,OAAO,IAAI,GAAG,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF","sourcesContent":["/**\n * Role in a conversation message.\n */\nexport type LLMRole = 'system' | 'user' | 'assistant';\n\n/**\n * A single message in a conversation.\n */\nexport type LLMMessage = {\n role: LLMRole;\n content: string;\n};\n\n/**\n * Options for sendMessage.\n */\nexport type LLMSendOptions = {\n /**\n * Default system prompt to use if no system message is provided in the messages array.\n * If a system message exists in the array, this is ignored.\n */\n systemPrompt?: string;\n};\n\n/**\n * Response from sendMessage.\n */\nexport type LLMResponse = {\n /** The generated response text */\n text: string;\n};\n\n/**\n * Options for streamMessage.\n */\nexport type LLMStreamOptions = {\n /**\n * Default system prompt to use if no system message is provided in the messages array.\n * If a system message exists in the array, this is ignored.\n */\n systemPrompt?: string;\n};\n\n/**\n * Event payload for streaming tokens.\n */\nexport type LLMStreamEvent = {\n /** Unique identifier for this streaming session */\n sessionId: string;\n /** The token/chunk of text received */\n token: string;\n /** Accumulated text so far */\n accumulatedText: string;\n /** Whether this is the final chunk */\n isDone: boolean;\n};\n\n/**\n * Callback function for streaming events.\n */\nexport type LLMStreamCallback = (event: LLMStreamEvent) => void;\n\n// ============================================================================\n// Prompt Helper Types\n// ============================================================================\n\n/**\n * Options for the summarize helper.\n */\nexport type LLMSummarizeOptions = {\n /**\n * Target length for the summary.\n * @default 'medium'\n */\n length?: 'short' | 'medium' | 'long';\n /**\n * Style of summary to generate.\n * @default 'paragraph'\n */\n style?: 'paragraph' | 'bullets' | 'tldr';\n};\n\n/**\n * Options for the translate helper.\n */\nexport type LLMTranslateOptions = {\n /**\n * Target language to translate to.\n */\n to: string;\n /**\n * Source language (auto-detected if not provided).\n */\n from?: string;\n /**\n * Tone/formality of the translation.\n * @default 'neutral'\n */\n tone?: 'formal' | 'informal' | 'neutral';\n};\n\n/**\n * Options for the rewrite helper.\n */\nexport type LLMRewriteOptions = {\n /**\n * Style to rewrite in.\n */\n style:\n | 'formal'\n | 'casual'\n | 'professional'\n | 'friendly'\n | 'concise'\n | 'detailed'\n | 'simple'\n | 'academic';\n};\n\n/**\n * Options for the extractKeyPoints helper.\n */\nexport type LLMExtractKeyPointsOptions = {\n /**\n * Maximum number of key points to extract.\n * @default 5\n */\n maxPoints?: number;\n};\n\n/**\n * Options for the answerQuestion helper.\n */\nexport type LLMAnswerQuestionOptions = {\n /**\n * How detailed the answer should be.\n * @default 'medium'\n */\n detail?: 'brief' | 'medium' | 'detailed';\n};\n\n// ============================================================================\n// Smart Suggestions Types\n// ============================================================================\n\n/**\n * Options for the suggest helper.\n */\nexport type LLMSuggestOptions = {\n /**\n * Number of suggestions to generate.\n * @default 3\n */\n count?: number;\n /**\n * Optional context to inform the suggestions (e.g., surrounding text, app context).\n */\n context?: string;\n /**\n * Tone of the suggestions.\n * @default 'neutral'\n */\n tone?: 'formal' | 'casual' | 'professional' | 'friendly' | 'neutral';\n};\n\n/**\n * A single suggestion item.\n */\nexport type LLMSuggestion = {\n /** The suggested text */\n text: string;\n};\n\n/**\n * Response from suggest/smartReply containing multiple suggestions.\n */\nexport type LLMSuggestResponse = {\n /** Array of generated suggestions */\n suggestions: LLMSuggestion[];\n /** Raw response text from the model */\n raw: string;\n};\n\n/**\n * Options for the smartReply helper.\n */\nexport type LLMSmartReplyOptions = {\n /**\n * Number of reply suggestions to generate.\n * @default 3\n */\n count?: number;\n /**\n * Tone of the reply suggestions.\n * @default 'neutral'\n */\n tone?: 'formal' | 'casual' | 'professional' | 'friendly' | 'neutral';\n /**\n * Optional persona/context for the replier (e.g., \"customer support agent\", \"friendly colleague\").\n */\n persona?: string;\n};\n\n/**\n * Options for the autocomplete helper.\n */\nexport type LLMAutocompleteOptions = {\n /**\n * Number of completions to generate.\n * @default 3\n */\n count?: number;\n /**\n * Maximum length of each completion in words.\n * @default 10\n */\n maxWords?: number;\n /**\n * Optional context to inform the completions (e.g., what the user is writing about).\n */\n context?: string;\n};\n\n// ============================================================================\n// Hook Types\n// ============================================================================\n\n/**\n * Options for the useChat hook.\n */\nexport type UseChatOptions = {\n /** System prompt for the AI assistant */\n systemPrompt?: string;\n /** Maximum conversation turns to keep in memory (default: 10) */\n maxTurns?: number;\n /** Initial messages to populate the chat */\n initialMessages?: LLMMessage[];\n /** Callback when a response is complete */\n onFinish?: (response: LLMResponse) => void;\n /** Callback when an error occurs */\n onError?: (error: Error) => void;\n};\n\n/**\n * Return type for the useChat hook.\n */\nexport type UseChatReturn = {\n /** All messages in the conversation */\n messages: LLMMessage[];\n /** Current input text value */\n input: string;\n /** Set the input text value */\n setInput: (input: string) => void;\n /** Send the current input (or provided text) as a message */\n sendMessage: (text?: string) => Promise<void>;\n /** Whether the AI is currently streaming a response */\n isStreaming: boolean;\n /** Stop the current streaming response */\n stop: () => void;\n /** Clear all messages and reset the conversation */\n clear: () => void;\n /** The most recent error, if any */\n error: Error | null;\n};\n\n/**\n * Options for the useCompletion hook.\n */\nexport type UseCompletionOptions = {\n /** System prompt for the AI */\n systemPrompt?: string;\n /** Callback when completion is done */\n onFinish?: (response: LLMResponse) => void;\n /** Callback when an error occurs */\n onError?: (error: Error) => void;\n};\n\n/**\n * Return type for the useCompletion hook.\n */\nexport type UseCompletionReturn = {\n /** The current completion text */\n completion: string;\n /** Whether a completion is in progress */\n isLoading: boolean;\n /** Request a completion for the given prompt */\n complete: (prompt: string) => Promise<string>;\n /** Stop the current completion */\n stop: () => void;\n /** The most recent error, if any */\n error: Error | null;\n};\n\n/**\n * Return type for the useOnDeviceAI hook.\n */\nexport type UseOnDeviceAIReturn = {\n /** Whether on-device AI is available */\n isAvailable: boolean;\n /** Whether the availability check is still in progress */\n isChecking: boolean;\n};\n\n// ============================================================================\n// Model Types\n// ============================================================================\n\n/**\n * A built-in model provided by the OS (e.g. Apple Foundation Models, ML Kit).\n * These are always available on supported devices -- no download needed.\n */\nexport type BuiltInModel = {\n /** Unique model identifier (e.g. 'apple-fm', 'mlkit') */\n id: string;\n /** Human-readable model name */\n name: string;\n /** Whether this model is available on the current device/OS */\n available: boolean;\n /** Platform this model is associated with */\n platform: 'ios' | 'android';\n /** Maximum context window in tokens */\n contextWindow: number;\n};\n\n/**\n * A downloadable model that the user manages (download, load, delete).\n * These require explicit download before use.\n */\nexport type DownloadableModel = {\n /** Unique model identifier (e.g. 'gemma-e2b', 'gemma-e4b') */\n id: string;\n /** Human-readable model name */\n name: string;\n /** Parameter count label (e.g. '2.3B') */\n parameterCount: string;\n /** Download file size in bytes */\n sizeBytes: number;\n /** Maximum context window in tokens */\n contextWindow: number;\n /** Minimum device RAM in bytes required to run */\n minRamBytes: number;\n /** Whether this device meets the model's minimum RAM requirement */\n meetsRequirements: boolean;\n /** Current lifecycle status */\n status: DownloadableModelStatus;\n};\n\n/**\n * Lifecycle status of a downloadable model.\n *\n * - 'not-downloaded': Model file is not on disk\n * - 'downloading': Model file is being downloaded\n * - 'loading': File is on disk, model is being loaded into memory for inference\n * - 'ready': Model is loaded in memory and ready for inference\n */\nexport type DownloadableModelStatus =\n | 'not-downloaded'\n | 'downloading'\n | 'loading'\n | 'ready';\n\n/**\n * Error codes for model-related operations.\n */\nexport type ModelErrorCode =\n | 'MODEL_NOT_FOUND'\n | 'MODEL_NOT_DOWNLOADED'\n | 'DOWNLOAD_FAILED'\n | 'DOWNLOAD_CORRUPT'\n | 'DOWNLOAD_STORAGE_FULL'\n | 'INFERENCE_OOM'\n | 'INFERENCE_FAILED'\n | 'MODEL_LOAD_FAILED'\n | 'DEVICE_NOT_SUPPORTED';\n\n/**\n * Structured error for model operations.\n */\nexport class ModelError extends Error {\n code: ModelErrorCode;\n modelId: string;\n\n constructor(code: ModelErrorCode, modelId: string, message?: string) {\n super(message ?? `${code}: ${modelId}`);\n this.name = 'ModelError';\n this.code = code;\n this.modelId = modelId;\n }\n}\n\n/**\n * Event payload for model download progress.\n */\nexport type ModelDownloadProgressEvent = {\n /** Model being downloaded */\n modelId: string;\n /** Download progress from 0 to 1 */\n progress: number;\n};\n\n/**\n * Event payload for model state changes.\n */\nexport type ModelStateChangeEvent = {\n /** Model whose state changed */\n modelId: string;\n /** New status */\n status: DownloadableModelStatus;\n};\n\n// ============================================================================\n// Chat Memory Types\n// ============================================================================\n\n/**\n * Options for creating a ChatMemoryManager.\n */\nexport type ChatMemoryOptions = {\n /**\n * Maximum number of conversation turns to keep in memory.\n * A turn is one user message + one assistant response.\n * @default 10\n */\n maxTurns?: number;\n /**\n * Optional system prompt to prepend to every generated prompt.\n */\n systemPrompt?: string;\n /**\n * Maximum context window in tokens. When set, trimHistory will also\n * trim messages that exceed this token budget (in addition to maxTurns).\n * Whichever limit is hit first triggers trimming.\n * @default Infinity (no token-based trimming)\n */\n contextWindow?: number;\n};\n\n/**\n * A snapshot of the current chat memory state.\n */\nexport type ChatMemorySnapshot = {\n /** All messages currently in memory */\n messages: LLMMessage[];\n /** The system prompt (if any) */\n systemPrompt: string | undefined;\n /** Current number of turns stored */\n turnCount: number;\n /** Maximum turns allowed */\n maxTurns: number;\n};\n"]}
@@ -5,11 +5,18 @@ public class ExpoAiKitModule: Module {
5
5
  // Track active streaming tasks for cancellation
6
6
  private var activeStreamTasks: [String: Task<Void, Never>] = [:]
7
7
 
8
+ // Active model ID: "apple-fm" (default) or a downloadable model ID
9
+ private var activeModelId: String = "apple-fm"
10
+
8
11
  public func definition() -> ModuleDefinition {
9
12
  Name("ExpoAiKit")
10
13
 
11
14
  // Declare events that can be sent to JavaScript
12
- Events("onStreamToken")
15
+ Events("onStreamToken", "onDownloadProgress", "onModelStateChange")
16
+
17
+ // ==================================================================
18
+ // Existing inference API -- Apple Foundation Models path unchanged
19
+ // ==================================================================
13
20
 
14
21
  Function("isAvailable") {
15
22
  if #available(iOS 26.0, *) {
@@ -145,5 +152,81 @@ public class ExpoAiKitModule: Module {
145
152
  self.activeStreamTasks.removeValue(forKey: sessionId)
146
153
  }
147
154
  }
155
+
156
+ // ==================================================================
157
+ // Model discovery
158
+ // ==================================================================
159
+
160
+ Function("getBuiltInModels") { () -> [[String: Any]] in
161
+ var available = false
162
+ if #available(iOS 26.0, *) {
163
+ available = true
164
+ }
165
+ return [
166
+ [
167
+ "id": "apple-fm",
168
+ "name": "Apple Foundation Model",
169
+ "available": available,
170
+ "platform": "ios",
171
+ // Apple FM context window is not publicly documented; use conservative default
172
+ "contextWindow": 4096
173
+ ]
174
+ ]
175
+ }
176
+
177
+ Function("getDownloadableModelStatus") { (modelId: String) -> String in
178
+ // No downloadable models supported on iOS yet (Phase 3: llama.cpp)
179
+ return "not-downloaded"
180
+ }
181
+
182
+ Function("getDeviceRamBytes") { () -> Int in
183
+ return Int(ProcessInfo.processInfo.physicalMemory)
184
+ }
185
+
186
+ // ==================================================================
187
+ // Model selection & memory management
188
+ // ==================================================================
189
+
190
+ AsyncFunction("setModel") { (modelId: String) in
191
+ if modelId == "apple-fm" {
192
+ self.activeModelId = "apple-fm"
193
+ return
194
+ }
195
+ // Downloadable models not yet supported on iOS
196
+ throw NSError(
197
+ domain: "ExpoAiKit",
198
+ code: 0,
199
+ userInfo: [NSLocalizedDescriptionKey: "MODEL_LOAD_FAILED:\(modelId):Downloadable models are not yet supported on iOS. Coming in a future release (Phase 3: llama.cpp)."]
200
+ )
201
+ }
202
+
203
+ Function("getActiveModel") { () -> String in
204
+ return self.activeModelId
205
+ }
206
+
207
+ AsyncFunction("unloadModel") { () in
208
+ // No downloadable model can be loaded on iOS yet; just reset to default
209
+ self.activeModelId = "apple-fm"
210
+ }
211
+
212
+ // ==================================================================
213
+ // Model lifecycle (downloadable models only -- stubs for iOS)
214
+ // ==================================================================
215
+
216
+ AsyncFunction("downloadModel") { (modelId: String, url: String, sha256: String) in
217
+ throw NSError(
218
+ domain: "ExpoAiKit",
219
+ code: 0,
220
+ userInfo: [NSLocalizedDescriptionKey: "DOWNLOAD_FAILED:\(modelId):Downloadable models are not yet supported on iOS. Coming in a future release (Phase 3: llama.cpp)."]
221
+ )
222
+ }
223
+
224
+ AsyncFunction("deleteModel") { (modelId: String) in
225
+ throw NSError(
226
+ domain: "ExpoAiKit",
227
+ code: 0,
228
+ userInfo: [NSLocalizedDescriptionKey: "MODEL_NOT_FOUND:\(modelId):Downloadable models are not yet supported on iOS. Coming in a future release (Phase 3: llama.cpp)."]
229
+ )
230
+ }
148
231
  }
149
232
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-ai-kit",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Expo AI Kit module",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -33,6 +33,7 @@ export interface ExpoAiKitNativeModule {
33
33
  // Model discovery
34
34
  getBuiltInModels(): BuiltInModel[];
35
35
  getDownloadableModelStatus(modelId: string): DownloadableModelStatus;
36
+ getDeviceRamBytes(): number;
36
37
 
37
38
  // Model selection & memory management
38
39
  // setModel is async: switching to a downloadable model loads it into memory.
package/src/index.ts CHANGED
@@ -1177,6 +1177,13 @@ export async function getDownloadableModels(): Promise<DownloadableModel[]> {
1177
1177
  entry.supportedPlatforms.includes(Platform.OS as 'ios' | 'android')
1178
1178
  );
1179
1179
 
1180
+ let deviceRamBytes = 0;
1181
+ try {
1182
+ deviceRamBytes = ExpoAiKitModule.getDeviceRamBytes();
1183
+ } catch {
1184
+ // Native call unavailable -- default to 0 (all models will show meetsRequirements: false)
1185
+ }
1186
+
1180
1187
  return platformModels.map((entry) => {
1181
1188
  const status = ExpoAiKitModule.getDownloadableModelStatus(entry.id);
1182
1189
  return {
@@ -1186,6 +1193,7 @@ export async function getDownloadableModels(): Promise<DownloadableModel[]> {
1186
1193
  sizeBytes: entry.sizeBytes,
1187
1194
  contextWindow: entry.contextWindow,
1188
1195
  minRamBytes: entry.minRamBytes,
1196
+ meetsRequirements: deviceRamBytes >= entry.minRamBytes,
1189
1197
  status,
1190
1198
  };
1191
1199
  });
@@ -1223,6 +1231,20 @@ export async function downloadModel(
1223
1231
  );
1224
1232
  }
1225
1233
 
1234
+ try {
1235
+ const deviceRamBytes = ExpoAiKitModule.getDeviceRamBytes();
1236
+ if (deviceRamBytes < entry.minRamBytes) {
1237
+ throw new ModelError(
1238
+ 'DEVICE_NOT_SUPPORTED',
1239
+ modelId,
1240
+ `Device has ${Math.round(deviceRamBytes / 1e9)}GB RAM, model requires ${Math.round(entry.minRamBytes / 1e9)}GB`
1241
+ );
1242
+ }
1243
+ } catch (e) {
1244
+ if (e instanceof ModelError) throw e;
1245
+ // If getDeviceRamBytes is unavailable, skip the check
1246
+ }
1247
+
1226
1248
  let subscription: ReturnType<typeof ExpoAiKitModule.addListener> | undefined;
1227
1249
  if (options?.onProgress) {
1228
1250
  subscription = ExpoAiKitModule.addListener(
package/src/types.ts CHANGED
@@ -339,6 +339,8 @@ export type DownloadableModel = {
339
339
  contextWindow: number;
340
340
  /** Minimum device RAM in bytes required to run */
341
341
  minRamBytes: number;
342
+ /** Whether this device meets the model's minimum RAM requirement */
343
+ meetsRequirements: boolean;
342
344
  /** Current lifecycle status */
343
345
  status: DownloadableModelStatus;
344
346
  };