@dr33m/react-native-litert-lm 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +627 -0
  3. package/android/CMakeLists.txt +32 -0
  4. package/android/build.gradle +104 -0
  5. package/android/src/main/AndroidManifest.xml +19 -0
  6. package/android/src/main/java/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLM.kt +888 -0
  7. package/android/src/main/java/com/margelo/nitro/dev/litert/litertlm/HybridModelStore.kt +189 -0
  8. package/android/src/main/java/com/margelo/nitro/dev/litert/litertlm/LiteRTLMRegistry.kt +32 -0
  9. package/android/src/main/java/com/margelo/nitro/dev/litert/litertlm/MultimodalPartFactories.kt +15 -0
  10. package/android/src/main/java/com/margelo/nitro/dev/litert/litertlm/StreamingCallbackListener.kt +89 -0
  11. package/android/src/main/java/dev/litert/litertlm/LiteRTLMInitProvider.kt +57 -0
  12. package/android/src/main/java/dev/litert/litertlm/LiteRTLMPackage.kt +43 -0
  13. package/android/src/test/java/com/margelo/nitro/core/Promise.kt +46 -0
  14. package/android/src/test/java/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLMTest.kt +122 -0
  15. package/app.plugin.js +58 -0
  16. package/cpp/cpp-adapter.cpp +21 -0
  17. package/cpp/include/README.md +32 -0
  18. package/cpp/include/litert_lm_engine.h +516 -0
  19. package/cpp/include/stb_image.h +7988 -0
  20. package/ios/HybridLiteRTLM+Execute.swift +287 -0
  21. package/ios/HybridLiteRTLM+Streaming.swift +153 -0
  22. package/ios/HybridLiteRTLM.swift +541 -0
  23. package/ios/HybridModelStore.swift +206 -0
  24. package/ios/MultimodalPart+Factories.swift +22 -0
  25. package/ios/Tests/HybridLiteRTLMTests.swift +187 -0
  26. package/lib/__mocks__/react-native-nitro-modules.d.ts +81 -0
  27. package/lib/__mocks__/react-native-nitro-modules.js +98 -0
  28. package/lib/__tests__/hooks.test.d.ts +1 -0
  29. package/lib/__tests__/hooks.test.js +175 -0
  30. package/lib/__tests__/inferenceRouting.test.d.ts +1 -0
  31. package/lib/__tests__/inferenceRouting.test.js +28 -0
  32. package/lib/__tests__/memoryTracker.test.d.ts +1 -0
  33. package/lib/__tests__/memoryTracker.test.js +74 -0
  34. package/lib/__tests__/modelFactory.test.d.ts +1 -0
  35. package/lib/__tests__/modelFactory.test.js +107 -0
  36. package/lib/__tests__/modelPath.test.d.ts +1 -0
  37. package/lib/__tests__/modelPath.test.js +12 -0
  38. package/lib/__tests__/modelRegistry.test.d.ts +1 -0
  39. package/lib/__tests__/modelRegistry.test.js +61 -0
  40. package/lib/hooks.d.ts +46 -0
  41. package/lib/hooks.js +159 -0
  42. package/lib/index.d.ts +142 -0
  43. package/lib/index.js +184 -0
  44. package/lib/inferenceRouting.d.ts +18 -0
  45. package/lib/inferenceRouting.js +63 -0
  46. package/lib/memoryTracker.d.ts +128 -0
  47. package/lib/memoryTracker.js +155 -0
  48. package/lib/modelFactory.d.ts +24 -0
  49. package/lib/modelFactory.js +128 -0
  50. package/lib/modelPath.d.ts +8 -0
  51. package/lib/modelPath.js +18 -0
  52. package/lib/modelRegistry.d.ts +55 -0
  53. package/lib/modelRegistry.js +82 -0
  54. package/lib/specs/LiteRTLM.nitro.d.ts +380 -0
  55. package/lib/specs/LiteRTLM.nitro.js +2 -0
  56. package/nitrogen/generated/.gitattributes +1 -0
  57. package/nitrogen/generated/android/LiteRTLM+autolinking.cmake +83 -0
  58. package/nitrogen/generated/android/LiteRTLM+autolinking.gradle +27 -0
  59. package/nitrogen/generated/android/LiteRTLMOnLoad.cpp +74 -0
  60. package/nitrogen/generated/android/LiteRTLMOnLoad.hpp +34 -0
  61. package/nitrogen/generated/android/c++/JBackend.hpp +61 -0
  62. package/nitrogen/generated/android/c++/JExecuteResult.hpp +86 -0
  63. package/nitrogen/generated/android/c++/JFunc_void_double.hpp +75 -0
  64. package/nitrogen/generated/android/c++/JFunc_void_std__string_bool.hpp +76 -0
  65. package/nitrogen/generated/android/c++/JGenerationStats.hpp +77 -0
  66. package/nitrogen/generated/android/c++/JHybridLiteRTLMSpec.cpp +371 -0
  67. package/nitrogen/generated/android/c++/JHybridLiteRTLMSpec.hpp +83 -0
  68. package/nitrogen/generated/android/c++/JHybridModelStoreSpec.cpp +101 -0
  69. package/nitrogen/generated/android/c++/JHybridModelStoreSpec.hpp +67 -0
  70. package/nitrogen/generated/android/c++/JLLMConfig.hpp +125 -0
  71. package/nitrogen/generated/android/c++/JMemoryUsage.hpp +69 -0
  72. package/nitrogen/generated/android/c++/JMessage.hpp +63 -0
  73. package/nitrogen/generated/android/c++/JModelFile.hpp +69 -0
  74. package/nitrogen/generated/android/c++/JMultimodalPart.hpp +78 -0
  75. package/nitrogen/generated/android/c++/JPartType.hpp +61 -0
  76. package/nitrogen/generated/android/c++/JRole.hpp +61 -0
  77. package/nitrogen/generated/android/c++/JToolCall.hpp +61 -0
  78. package/nitrogen/generated/android/c++/JToolDefinition.hpp +65 -0
  79. package/nitrogen/generated/android/c++/JToolResponse.hpp +61 -0
  80. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/Backend.kt +24 -0
  81. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/ExecuteResult.kt +61 -0
  82. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/Func_void_double.kt +80 -0
  83. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/Func_void_std__string_bool.kt +80 -0
  84. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/GenerationStats.kt +76 -0
  85. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLMSpec.kt +165 -0
  86. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/HybridModelStoreSpec.kt +76 -0
  87. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/LLMConfig.kt +106 -0
  88. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/LiteRTLMOnLoad.kt +35 -0
  89. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/MemoryUsage.kt +66 -0
  90. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/Message.kt +56 -0
  91. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/ModelFile.kt +66 -0
  92. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/MultimodalPart.kt +71 -0
  93. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/PartType.kt +24 -0
  94. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/Role.kt +24 -0
  95. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/ToolCall.kt +56 -0
  96. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/ToolDefinition.kt +61 -0
  97. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/ToolResponse.kt +56 -0
  98. package/nitrogen/generated/ios/LiteRTLM+autolinking.rb +62 -0
  99. package/nitrogen/generated/ios/LiteRTLM-Swift-Cxx-Bridge.cpp +98 -0
  100. package/nitrogen/generated/ios/LiteRTLM-Swift-Cxx-Bridge.hpp +585 -0
  101. package/nitrogen/generated/ios/LiteRTLM-Swift-Cxx-Umbrella.hpp +93 -0
  102. package/nitrogen/generated/ios/LiteRTLMAutolinking.mm +41 -0
  103. package/nitrogen/generated/ios/LiteRTLMAutolinking.swift +38 -0
  104. package/nitrogen/generated/ios/c++/HybridLiteRTLMSpecSwift.cpp +11 -0
  105. package/nitrogen/generated/ios/c++/HybridLiteRTLMSpecSwift.hpp +279 -0
  106. package/nitrogen/generated/ios/c++/HybridModelStoreSpecSwift.cpp +11 -0
  107. package/nitrogen/generated/ios/c++/HybridModelStoreSpecSwift.hpp +117 -0
  108. package/nitrogen/generated/ios/swift/Backend.swift +44 -0
  109. package/nitrogen/generated/ios/swift/ExecuteResult.swift +45 -0
  110. package/nitrogen/generated/ios/swift/Func_void.swift +46 -0
  111. package/nitrogen/generated/ios/swift/Func_void_ExecuteResult.swift +46 -0
  112. package/nitrogen/generated/ios/swift/Func_void_double.swift +46 -0
  113. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
  114. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +46 -0
  115. package/nitrogen/generated/ios/swift/Func_void_std__string_bool.swift +46 -0
  116. package/nitrogen/generated/ios/swift/GenerationStats.swift +54 -0
  117. package/nitrogen/generated/ios/swift/HybridLiteRTLMSpec.swift +75 -0
  118. package/nitrogen/generated/ios/swift/HybridLiteRTLMSpec_cxx.swift +516 -0
  119. package/nitrogen/generated/ios/swift/HybridModelStoreSpec.swift +59 -0
  120. package/nitrogen/generated/ios/swift/HybridModelStoreSpec_cxx.swift +203 -0
  121. package/nitrogen/generated/ios/swift/LLMConfig.swift +239 -0
  122. package/nitrogen/generated/ios/swift/MemoryUsage.swift +44 -0
  123. package/nitrogen/generated/ios/swift/Message.swift +34 -0
  124. package/nitrogen/generated/ios/swift/ModelFile.swift +44 -0
  125. package/nitrogen/generated/ios/swift/MultimodalPart.swift +101 -0
  126. package/nitrogen/generated/ios/swift/PartType.swift +44 -0
  127. package/nitrogen/generated/ios/swift/Role.swift +44 -0
  128. package/nitrogen/generated/ios/swift/ToolCall.swift +34 -0
  129. package/nitrogen/generated/ios/swift/ToolDefinition.swift +39 -0
  130. package/nitrogen/generated/ios/swift/ToolResponse.swift +34 -0
  131. package/nitrogen/generated/shared/c++/Backend.hpp +80 -0
  132. package/nitrogen/generated/shared/c++/ExecuteResult.hpp +94 -0
  133. package/nitrogen/generated/shared/c++/GenerationStats.hpp +103 -0
  134. package/nitrogen/generated/shared/c++/HybridLiteRTLMSpec.cpp +41 -0
  135. package/nitrogen/generated/shared/c++/HybridLiteRTLMSpec.hpp +109 -0
  136. package/nitrogen/generated/shared/c++/HybridModelStoreSpec.cpp +25 -0
  137. package/nitrogen/generated/shared/c++/HybridModelStoreSpec.hpp +71 -0
  138. package/nitrogen/generated/shared/c++/LLMConfig.hpp +134 -0
  139. package/nitrogen/generated/shared/c++/MemoryUsage.hpp +95 -0
  140. package/nitrogen/generated/shared/c++/Message.hpp +89 -0
  141. package/nitrogen/generated/shared/c++/ModelFile.hpp +95 -0
  142. package/nitrogen/generated/shared/c++/MultimodalPart.hpp +103 -0
  143. package/nitrogen/generated/shared/c++/PartType.hpp +80 -0
  144. package/nitrogen/generated/shared/c++/Role.hpp +80 -0
  145. package/nitrogen/generated/shared/c++/ToolCall.hpp +87 -0
  146. package/nitrogen/generated/shared/c++/ToolDefinition.hpp +91 -0
  147. package/nitrogen/generated/shared/c++/ToolResponse.hpp +87 -0
  148. package/package.json +115 -0
  149. package/react-native-litert-lm.podspec +62 -0
  150. package/react-native.config.js +16 -0
  151. package/scripts/download-ios-frameworks.sh +39 -0
  152. package/scripts/framework-source.js +46 -0
  153. package/scripts/postinstall.js +145 -0
  154. package/src/__mocks__/react-native-nitro-modules.ts +132 -0
  155. package/src/__tests__/hooks.test.ts +216 -0
  156. package/src/__tests__/inferenceRouting.test.ts +36 -0
  157. package/src/__tests__/memoryTracker.test.ts +87 -0
  158. package/src/__tests__/modelFactory.test.ts +153 -0
  159. package/src/__tests__/modelPath.test.ts +14 -0
  160. package/src/__tests__/modelRegistry.test.ts +73 -0
  161. package/src/hooks.ts +234 -0
  162. package/src/index.ts +214 -0
  163. package/src/inferenceRouting.ts +80 -0
  164. package/src/memoryTracker.ts +268 -0
  165. package/src/modelFactory.ts +163 -0
  166. package/src/modelPath.ts +16 -0
  167. package/src/modelRegistry.ts +104 -0
  168. package/src/specs/LiteRTLM.nitro.ts +451 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 hung-yueh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,627 @@
1
+ # react-native-litert-lm
2
+
3
+ High-performance on-device LLM inference for React Native, powered by [LiteRT-LM](https://github.com/google-ai-edge/LiteRT-LM) and [Nitro Modules](https://github.com/mrousavy/nitro). Optimized for **Gemma 4** and other on-device language models.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **Native Swift Bridge (iOS)** — Bypasses Swift actor deadlocks (User Rule #1) via direct C FFI dispatched on a serial `dev.litert.engine` background queue.
8
+ - 🤖 **Stateless Kotlin Bridge (Android)** — Fully conforms to `HybridLiteRTLMSpec` using direct JSI memory access.
9
+ - ⚡ **Zero-Copy Multimodal API** — Native-owned `ArrayBuffer` mapping straight to FFI inputs for image/audio data without copy overhead (complying with User Rule #2).
10
+ - 🧠 **Speculative Decoding** — Active multi-token prediction support with pre-flight model capability validation.
11
+ - 🛠️ **Function / Tool Calling** — Native JSON-encoded schema specification support for structured outputs.
12
+ - 🏎️ **GPU Acceleration** — Metal (iOS), OpenCL GPU delegate (Android, Pixel devices).
13
+ - 🔄 **Streaming Support** — Non-blocking token-by-token callbacks.
14
+ - 📊 **Real Memory Tracking** — OS-level memory metrics (RSS, native heap, available memory) via native APIs (complying with User Rule #3).
15
+ - 📥 **Automatic Model Download** — Downloads models from URL with progress tracking and local caching.
16
+
17
+ ## Demo
18
+
19
+ > Gemma 4 E2B running on-device on a Samsung Galaxy S22 (Snapdragon 8 Gen 1, 4 GB RAM) — CPU backend, streaming inference.
20
+
21
+ <video src="https://github.com/user-attachments/assets/1da527ce-0432-4f8b-8899-474f81b2feea" width="300" controls></video>
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install react-native-litert-lm react-native-nitro-modules
27
+ ```
28
+
29
+ ### Expo
30
+
31
+ Add to your `app.json`:
32
+
33
+ ```json
34
+ {
35
+ "expo": {
36
+ "plugins": ["react-native-litert-lm"],
37
+ "android": {
38
+ "minSdkVersion": 26
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ Then create a development build:
45
+
46
+ ```bash
47
+ npx expo prebuild
48
+ npx expo run:android # Android
49
+ npx expo run:ios # iOS
50
+ ```
51
+
52
+ > **Note**: Only ARM devices/simulators are supported. x86_64 Android emulators are not supported.
53
+
54
+ ### Bare React Native
55
+
56
+ ```bash
57
+ # Android
58
+ cd android && ./gradlew clean
59
+
60
+ # iOS
61
+ cd ios && pod install
62
+ ```
63
+
64
+ ## Example App
65
+
66
+ The `example/` directory contains a fully functional test app with a dark-themed diagnostic UI that demonstrates:
67
+
68
+ - Model downloading with progress tracking
69
+ - Text inference (blocking and streaming)
70
+ - Multi-turn conversation with context retention
71
+ - Performance benchmarking (tokens/sec, latency)
72
+ - Real-time memory tracking
73
+ - Speculative decoding & tool calling settings toggles
74
+ - Zero-copy multimodal inference loading images/audio directly into ArrayBuffers
75
+
76
+ ### Running the Example
77
+
78
+ 1. **Build the library** (compiles TypeScript to `lib/`):
79
+
80
+ ```bash
81
+ npm run build
82
+ ```
83
+
84
+ 2. **Install example dependencies:**
85
+
86
+ ```bash
87
+ cd example
88
+ npm install
89
+ ```
90
+
91
+ 3. **Create a development build and run:**
92
+
93
+ ```bash
94
+ npx expo prebuild --clean
95
+ npx expo run:android # Android
96
+ npx expo run:ios # iOS (pre-linked with CLiteRTLM.xcframework)
97
+ ```
98
+
99
+ > **Note:** If you change native code (Swift/Kotlin), you must run `npx expo prebuild --clean` again before rebuilding.
100
+
101
+ ## Model Management
102
+
103
+ LiteRT-LM models (like Gemma 4) are large files (1–4 GB) and cannot be bundled into your app binary. They are downloaded at runtime.
104
+
105
+ ### Automatic Downloading
106
+
107
+ Pass an HTTPS URL to `useModel()` or `loadModel()` — the library handles the rest:
108
+
109
+ - **Progress tracking** — real-time download percentage via callbacks
110
+ - **Local caching** — downloaded models are cached and reused across app launches
111
+ - **Android**: `files/models/` (app-private)
112
+ - **iOS**: `Library/Caches/litert_models/` (survives app relaunch; reclaimable by iOS under storage pressure)
113
+ - **HTTPS enforcement** — only secure URLs are accepted
114
+
115
+ ### Manual Downloading
116
+
117
+ If you need custom control over downloads (e.g., authentication headers for private model hosting, resumable downloads, or custom caching), use your preferred HTTP client and pass the local file path:
118
+
119
+ ```typescript
120
+ import { fetch } from "expo/fetch";
121
+ import { File, Paths } from "expo-file-system";
122
+ import { useModel } from "react-native-litert-lm";
123
+
124
+ const MODEL_URL = "https://example.com/private-model.litertlm";
125
+
126
+ // Download with custom headers using expo/fetch
127
+ const response = await fetch(MODEL_URL, {
128
+ headers: { Authorization: `Bearer ${token}` },
129
+ });
130
+ const modelFile = new File(Paths.cache, "my-model.litertlm");
131
+ modelFile.write(await response.bytes());
132
+
133
+ // Pass the local path — no download occurs
134
+ const { model, isReady } = useModel(modelFile.uri, { backend: "cpu" });
135
+ ```
136
+
137
+ ## Usage
138
+
139
+ ### React Hook (Recommended)
140
+
141
+ The `useModel` hook manages the full model lifecycle: downloading, loading, inference, and cleanup.
142
+
143
+ ```typescript
144
+ import { useModel, GEMMA_4_E2B_IT } from "react-native-litert-lm";
145
+ import { Platform } from "react-native";
146
+
147
+ function App() {
148
+ const {
149
+ model,
150
+ isReady,
151
+ downloadProgress,
152
+ error,
153
+ load, // Manually trigger load
154
+ deleteModel, // Delete cached model file
155
+ memorySummary, // Auto-updated memory stats (if tracking enabled)
156
+ } = useModel(GEMMA_4_E2B_IT, {
157
+ backend: 'cpu',
158
+ autoLoad: true, // Default: true. Set false to load manually via load().
159
+ systemPrompt: "You are a helpful assistant.",
160
+ enableMemoryTracking: true,
161
+ });
162
+
163
+ if (!isReady) {
164
+ return <Text>Loading... {Math.round(downloadProgress * 100)}%</Text>;
165
+ }
166
+
167
+ const generate = async () => {
168
+ const response = await model.sendMessage("Hello!");
169
+ console.log(response);
170
+ };
171
+
172
+ return <Button title="Generate" onPress={generate} />;
173
+ }
174
+ ```
175
+
176
+ ### Manual Usage
177
+
178
+ ```typescript
179
+ import { createLLM } from "react-native-litert-lm";
180
+
181
+ const llm = createLLM();
182
+
183
+ // Load a model from URL (auto-downloads) or local path
184
+ await llm.loadModel("https://example.com/model.litertlm", {
185
+ backend: "gpu",
186
+ systemPrompt: "You are a helpful assistant.",
187
+ });
188
+
189
+ // Generate a response
190
+ const response = await llm.sendMessage("What is the capital of France?");
191
+ console.log(response);
192
+
193
+ // Clean up
194
+ llm.close();
195
+ ```
196
+
197
+ ### Streaming Generation
198
+
199
+ ```typescript
200
+ llm.sendMessageAsync("Tell me a story", (token, done) => {
201
+ process.stdout.write(token);
202
+ if (done) console.log("\n--- Done ---");
203
+ });
204
+ ```
205
+
206
+ ### Multimodal (Image / Audio) & Zero-Copy Buffers
207
+
208
+ Multimodal features are fully supported via standard file paths or high-performance zero-copy `ArrayBuffer` objects:
209
+
210
+ #### 1. Zero-Copy Multimodal Messages (Recommended)
211
+ This API uses Nitro Modules' native-backed `ArrayBuffer` directly mapped to native memory buffers, avoiding any base64 heap copying overhead (User Rule #2):
212
+
213
+ ```typescript
214
+ import { checkMultimodalSupport } from "react-native-litert-lm";
215
+
216
+ const warning = checkMultimodalSupport();
217
+ if (warning) {
218
+ console.warn(warning); // Experimental or unsupported on current platform (e.g. iOS simulator)
219
+ } else {
220
+ // Read local assets or files straight into ArrayBuffers using fetch
221
+ const response = await fetch(Image.resolveAssetSource(require("./test.jpeg")).uri);
222
+ const imageBuffer = await response.arrayBuffer();
223
+
224
+ const reply = await llm.sendMultimodalMessage([
225
+ { type: "image", imageBuffer },
226
+ { type: "text", text: "Describe what is in this image." }
227
+ ]);
228
+ console.log(reply);
229
+ }
230
+ ```
231
+
232
+ #### 2. Path-Based Multimodal Messages
233
+ ```typescript
234
+ // Image input
235
+ const response = await llm.sendMessageWithImage(
236
+ "What's in this image?",
237
+ "/path/to/image.jpg",
238
+ );
239
+
240
+ // Audio input
241
+ const transcription = await llm.sendMessageWithAudio(
242
+ "Transcribe this audio",
243
+ "/path/to/audio.wav",
244
+ );
245
+ ```
246
+
247
+ ### Speculative Decoding & Tools
248
+
249
+ #### 1. Speculative Decoding (MTP)
250
+ Enable speculative decoding in `LLMConfig` to accelerate inference using multi-token prediction when supported by your model:
251
+
252
+ ```typescript
253
+ const { model } = useModel(GEMMA_4_E2B_IT, {
254
+ enableSpeculativeDecoding: true,
255
+ });
256
+ ```
257
+
258
+ #### 2. Function / Tool Calling
259
+ Inject tools as an array of definitions, specifying parameter validation using standard JSON schema format:
260
+
261
+ ```typescript
262
+ const { model } = useModel(GEMMA_4_E2B_IT, {
263
+ tools: [
264
+ {
265
+ name: "get_current_weather",
266
+ description: "Get the current weather for a location",
267
+ parametersJson: JSON.stringify({
268
+ type: "object",
269
+ properties: {
270
+ location: { type: "string", description: "The city and state, e.g. San Francisco, CA" },
271
+ unit: { type: "string", enum: ["celsius", "fahrenheit"] }
272
+ },
273
+ required: ["location"]
274
+ })
275
+ }
276
+ ]
277
+ });
278
+ ```
279
+
280
+ ### Performance Stats
281
+
282
+ ```typescript
283
+ const stats = llm.getStats();
284
+ console.log(`Generated ${stats.completionTokens} tokens`);
285
+ console.log(`Speed: ${stats.tokensPerSecond.toFixed(1)} tokens/sec`);
286
+ console.log(`Time to first token: ${stats.timeToFirstToken.toFixed(0)} ms`);
287
+ ```
288
+
289
+ > **Note**: Stats are available for both sync (`sendMessage`) and streaming (`sendMessageAsync`) on both platforms. iOS uses real benchmark data from the C API; Android uses heuristic token counts with precise timing.
290
+
291
+ ### Memory Tracking
292
+
293
+ The library provides real OS-level memory data — no estimation. It reads directly from `mach_task_basic_info` (iOS) and `Debug.getNativeHeapAllocatedSize()` + `/proc/self/status` (Android).
294
+
295
+ #### Direct Memory Query
296
+
297
+ ```typescript
298
+ const usage = llm.getMemoryUsage();
299
+ console.log(
300
+ `Native heap: ${(usage.nativeHeapBytes / 1024 / 1024).toFixed(1)} MB`,
301
+ );
302
+ console.log(`RSS: ${(usage.residentBytes / 1024 / 1024).toFixed(1)} MB`);
303
+ console.log(
304
+ `Available: ${(usage.availableMemoryBytes / 1024 / 1024).toFixed(1)} MB`,
305
+ );
306
+ console.log(`Low memory: ${usage.isLowMemory}`);
307
+ ```
308
+
309
+ #### Automatic Tracking with Native Buffers
310
+
311
+ Enable memory tracking to automatically record snapshots in a native-backed `ArrayBuffer` after every inference call:
312
+
313
+ ```typescript
314
+ const llm = createLLM({
315
+ enableMemoryTracking: true,
316
+ maxMemorySnapshots: 256,
317
+ });
318
+
319
+ await llm.loadModel("/path/to/model.litertlm", { backend: "cpu" });
320
+ await llm.sendMessage("Hello!");
321
+
322
+ const summary = llm.memoryTracker!.getSummary();
323
+ console.log(
324
+ `Peak RSS: ${(summary.peakResidentBytes / 1024 / 1024).toFixed(1)} MB`,
325
+ );
326
+ console.log(
327
+ `RSS Delta: ${(summary.residentDeltaBytes / 1024 / 1024).toFixed(1)} MB`,
328
+ );
329
+ ```
330
+
331
+ #### Using `useModel` with Memory Tracking
332
+
333
+ ```typescript
334
+ const { model, isReady, memorySummary } = useModel(modelUrl, {
335
+ enableMemoryTracking: true,
336
+ maxMemorySnapshots: 100,
337
+ });
338
+
339
+ // memorySummary auto-updates after each inference call
340
+ if (memorySummary) {
341
+ console.log(`Current RSS: ${memorySummary.currentResidentBytes}`);
342
+ console.log(`Peak RSS: ${memorySummary.peakResidentBytes}`);
343
+ }
344
+ ```
345
+
346
+ #### Standalone Memory Tracker
347
+
348
+ ```typescript
349
+ import {
350
+ createMemoryTracker,
351
+ createNativeBuffer,
352
+ } from "react-native-litert-lm";
353
+
354
+ const tracker = createMemoryTracker(100);
355
+
356
+ tracker.record({
357
+ timestamp: Date.now(),
358
+ nativeHeapBytes: 50_000_000,
359
+ residentBytes: 200_000_000,
360
+ availableMemoryBytes: 4_000_000_000,
361
+ });
362
+
363
+ // Access the underlying native buffer (zero-copy transfer to native code)
364
+ const buffer = tracker.getNativeBuffer();
365
+ ```
366
+
367
+ ## Supported Models
368
+
369
+ All exported model URLs are **public — no authentication required**. Pass them directly to `useModel()` or `loadModel()` for automatic downloading with progress tracking and local caching.
370
+
371
+ | Constant | Model | Size | Min RAM | Source |
372
+ | :--------------------- | :------------------------------ | :------ | :------ | :---------- |
373
+ | `GEMMA_4_E2B_IT` | Gemma 4 E2B (Multimodal, IT) | 2.58 GB | 4 GB+ | HuggingFace |
374
+ | `GEMMA_4_E4B_IT` | Gemma 4 E4B (Higher Quality) | 3.65 GB | 6 GB+ | HuggingFace |
375
+ | `GEMMA_3N_E2B_IT_INT4` | Gemma 3n E2B (Int4, Multimodal) | ~1.3 GB | 4 GB+ | litert.dev |
376
+
377
+ > **Recommended:** Use `GEMMA_4_E2B_IT` for most use cases — multimodal (text + vision + audio) and the best quality-to-size ratio.
378
+ >
379
+ > **iOS Note:** Models larger than ~2 GB require the `com.apple.developer.kernel.extended-virtual-addressing` entitlement. See [iOS Entitlements](#ios-entitlements) below. Gemma 3n E2B (~1.3 GB) works without it.
380
+
381
+ **Other compatible models** (download `.litertlm` files manually from [HuggingFace](https://huggingface.co/litert-community)):
382
+
383
+ | Model | Size | Min RAM | Notes |
384
+ | ------------- | ------- | ------- | --------------------- |
385
+ | Gemma 3 1B | ~1 GB | 4 GB+ | Smallest, fastest |
386
+ | Phi-4 Mini | ~2 GB | 4 GB+ | Microsoft's small LLM |
387
+ | Qwen 2.5 1.5B | ~1.5 GB | 4 GB+ | Multilingual |
388
+
389
+ ## API Reference
390
+
391
+ ### `createLLM(options?): LiteRTLM`
392
+
393
+ Creates a new LLM inference engine instance.
394
+
395
+ - `options.enableMemoryTracking` — enable automatic memory snapshot recording
396
+ - `options.maxMemorySnapshots` — max number of snapshots to retain (default: 256)
397
+
398
+ ### `loadModel(path, config?): Promise<void>`
399
+
400
+ Loads a model from a local path or HTTPS URL.
401
+
402
+ | Parameter | Type | Default | Description |
403
+ | --------------------- | -------- | ------- | ----------------------------------------- |
404
+ | `path` | `string` | — | Absolute path to `.litertlm` or HTTPS URL |
405
+ | `config.backend` | `string` | `'cpu'` | `'cpu'`, `'gpu'`, or `'npu'` |
406
+ | `config.systemPrompt` | `string` | — | System prompt for the model |
407
+ | `config.temperature` | `number` | `0.7` | Sampling temperature |
408
+ | `config.topK` | `number` | `40` | Top-K sampling |
409
+ | `config.topP` | `number` | `0.95` | Top-P (nucleus) sampling |
410
+ | `config.maxTokens` | `number` | `1024` | Maximum generation length |
411
+
412
+ #### Backend Options
413
+
414
+ | Backend | Engine | Speed | Notes |
415
+ | ------- | ------------------------------ | ------- | ---------------------------------------------------------------------------------- |
416
+ | `'cpu'` | CPU inference | Slowest | Always available on all devices |
417
+ | `'gpu'` | Metal (iOS) / OpenCL (Android) | Fast | iOS: always available. Android: requires OpenCL (Pixel only, not Samsung/Qualcomm) |
418
+ | `'npu'` | NPU / Neural Engine | Fastest | Requires supported hardware; experimental |
419
+
420
+ > **iOS**: Both `'cpu'` and `'gpu'` (Metal) are supported. The engine automatically tries fallback backend combinations if the primary one fails.
421
+ >
422
+ > **Android GPU**: The GPU backend requires OpenCL, which is **not available on most Samsung and Qualcomm devices**. Use `checkBackendSupport('gpu')` to check before loading. The engine will throw a clear error if GPU is unsupported.
423
+
424
+ ### `sendMessage(message): Promise<string>`
425
+
426
+ Runs inference synchronously on a background thread. Returns the complete response.
427
+
428
+ ### `sendMessageAsync(message, callback)`
429
+
430
+ Streaming generation. Callback signature: `(token: string, isDone: boolean) => void`.
431
+
432
+ ### `sendMessageWithImage(message, imagePath): Promise<string>`
433
+
434
+ Send a message with an image (for vision models like Gemma 4 E2B).
435
+
436
+ ### `sendMessageWithAudio(message, audioPath): Promise<string>`
437
+
438
+ Send a message with audio (for audio-capable models like Gemma 4 E2B).
439
+
440
+ ### `getStats(): GenerationStats`
441
+
442
+ Returns performance metrics from the last inference call.
443
+
444
+ ### `getMemoryUsage(): MemoryUsage`
445
+
446
+ Returns real OS-level memory usage.
447
+
448
+ ### `getHistory(): Message[]`
449
+
450
+ Returns the conversation history.
451
+
452
+ ### `resetConversation()`
453
+
454
+ Clears conversation context and starts a fresh session.
455
+
456
+ ### `close()`
457
+
458
+ Releases all native resources. Call when the model is no longer needed.
459
+
460
+ ### `deleteModel(fileName): Promise<void>`
461
+
462
+ Deletes a cached model file from the app's local storage.
463
+
464
+ ### Utility Functions
465
+
466
+ ```typescript
467
+ import {
468
+ checkBackendSupport,
469
+ checkMultimodalSupport,
470
+ getRecommendedBackend,
471
+ } from "react-native-litert-lm";
472
+
473
+ // Check if GPU is supported on this device
474
+ const gpuWarning = checkBackendSupport("gpu");
475
+
476
+ // Check NPU support
477
+ const npuWarning = checkBackendSupport("npu"); // string | undefined
478
+
479
+ // Check multimodal support
480
+ const mmError = checkMultimodalSupport(); // string | undefined
481
+
482
+ // Get recommended backend
483
+ const backend = getRecommendedBackend(); // 'cpu'
484
+ ```
485
+
486
+ ## Requirements
487
+
488
+ | Dependency | Version |
489
+ | -------------------------- | ------------- |
490
+ | React Native | 0.76+ |
491
+ | react-native-nitro-modules | 0.35.0+ |
492
+ | Android API | 26+ (ARM64) |
493
+ | iOS | 15.0+ (ARM64) |
494
+ | LiteRT-LM Engine | 0.12.0 |
495
+
496
+ ## Platform Support
497
+
498
+ | Platform | Status | Architecture | Backends |
499
+ | -------- | -------- | ------------ | ------------------------------------------------- |
500
+ | Android | ✅ Ready | arm64-v8a | CPU (all devices), GPU (OpenCL devices only), NPU |
501
+ | iOS | ✅ Ready | arm64 | CPU, GPU (Metal — always available) |
502
+
503
+ ### iOS Feature Matrix
504
+
505
+ | Feature | Status | Notes |
506
+ | ---------------------------- | ------ | ------------------------------------------------------ |
507
+ | Text inference (blocking) | ✅ | Direct FFI using `dev.litert.engine` background queue |
508
+ | Text inference (streaming) | ✅ | Token-by-token callbacks |
509
+ | CPU inference | ✅ | Safe fallback default |
510
+ | GPU inference (Metal/MPS) | ✅ | Supported via `backend: 'gpu'` |
511
+ | Model download with progress | ✅ | URLSession-based, cached in `Caches/` |
512
+ | Memory tracking | ✅ | Real-time Resident Set Size (RSS) tracking |
513
+ | Multi-turn conversation | ✅ | Context retained across turns |
514
+ | Multimodal (image/audio) | ✅ | Zero-copy `ArrayBuffer` mapping to FFI input buffers |
515
+ | Speculative Decoding | ✅ | Dynamic capabilities check during model pre-load |
516
+ | Function / Tool Calling | ✅ | Supported via JSON-encoded schema specification |
517
+
518
+ ### iOS Entitlements
519
+
520
+ Models larger than ~2 GB (like Gemma 4 E2B at 2.58 GB) require the **Extended Virtual Addressing** entitlement on iOS physical devices. Without it, iOS limits virtual memory to ~2 GB and the app will be killed by Jetsam.
521
+
522
+ Add to your app's `.entitlements` file:
523
+
524
+ ```xml
525
+ <key>com.apple.developer.kernel.extended-virtual-addressing</key>
526
+ <true/>
527
+ ```
528
+
529
+ > **Note:** This entitlement requires a **paid Apple Developer account** ($99/year). Gemma 3n E2B (~1.3 GB) works without it.
530
+
531
+ ## iOS FFI Architecture & Integration
532
+
533
+ The library uses a highly optimized Swift Direct-FFI bridge that links directly with the pre-compiled C library `CLiteRTLM.xcframework`.
534
+
535
+ ### Key Design Commitments
536
+
537
+ 1. **JSI Thread Safety (User Rule #1)**:
538
+ - The JSI/JS thread must never be blocked by native synchronous lock-waiting operations.
539
+ - We dispatch all FFI calls to a serial background `dev.litert.engine` queue, executing callbacks asynchronously to prevent deadlocking JSI execution.
540
+
541
+ 2. **Zero-Copy Memory Pipelines (User Rule #2)**:
542
+ - Enforce the use of Nitro Modules' `ArrayBuffer` directly referencing native memory pointers (`ArrayBuffer.data`) when processing heavy media assets like images or audio.
543
+
544
+ 3. **Manual FFI Resource Management (User Rule #3)**:
545
+ - Raw pointers (`LiteRtLmEngine*`, `LiteRtLmConversation*`) are manually allocated and strictly deallocated inside Swift `deinit` and `close()` destructors to guarantee 0% memory leaks during prolonged inference sessions.
546
+
547
+ ### Architecture Topology
548
+
549
+ ```
550
+ ┌──────────────────────────────────────────────────────────┐
551
+ │ React Native (TypeScript / JavaScript) │
552
+ ├──────────────────────────────────────────────────────────┤
553
+ │ Nitro Modules JSI Bindings (`HybridLiteRTLMSpec`) │
554
+ ├─────────────────────────────┬────────────────────────────┤
555
+ │ Android (Kotlin) │ iOS (Swift Direct FFI) │
556
+ │ `HybridLiteRTLM.kt` │ `HybridLiteRTLM.swift` │
557
+ │ `litertlm-android` AAR │ `CLiteRTLM.xcframework` │
558
+ └─────────────────────────────┴────────────────────────────┘
559
+ ```
560
+
561
+ #### Android Bridging
562
+ - Conforms fully to `HybridLiteRTLMSpec` using Kotlin.
563
+ - Incorporates Proguard keep rules to prevent dynamic JSI/JNI code stripping.
564
+ - Declares `<uses-native-library android:name="libOpenCL.so" android:required="false" />` to load dynamic OpenCL for GPU delegate acceleration on Android 12+ without throwing platform installer exceptions.
565
+
566
+ #### iOS Bridging
567
+ - Entirely written in native Swift (`HybridLiteRTLM.swift`) calling direct FFI.
568
+ - Avoids the upstream Swift SDK `actor` lock-blocking deadlocks by utilizing low-level C functions directly.
569
+ - Implements custom `getMemoryUsage` that queries the OS directly via `mach_task_basic_info` to get precise real-time Resident Set Size (RSS) metrics.
570
+
571
+ ## Testing
572
+
573
+ The library includes a comprehensive multi-tier unit testing suite designed to run quickly on host machines (CI runners or local development environments) without requiring a physical test device.
574
+
575
+ ### 1. JavaScript / TypeScript Layer (Jest)
576
+
577
+ The JS/TS layer uses Jest to validate the `useModel` hook, download progress callbacks, URL query scrubbing, file storage helpers, and the zero-copy native memory tracker buffer allocations.
578
+
579
+ * **Setup & Mocking**: Includes an active stub (`src/__mocks__/react-native-nitro-modules.ts`) that mocks the Nitro Modules `HybridObject` architecture.
580
+ * **How to run**:
581
+ ```bash
582
+ npm run test
583
+ ```
584
+
585
+ ### 2. Android Kotlin Layer (Robolectric)
586
+
587
+ The Android layer uses local JUnit Robolectric tests to run Android code on the JVM, sandboxing OS dependencies. It validates HTTPS schema constraints, path traversal mitigations, and initial telemetry states.
588
+
589
+ * **Setup & Mocking**: Uses a local shadow `Promise` implementation to test thread-asynchronous errors.
590
+ * **How to run**:
591
+ ```bash
592
+ cd example/android
593
+ ./gradlew :react-native-litert-lm:testDebugUnitTest
594
+ ```
595
+
596
+ ### 3. iOS Swift Layer (XCTest)
597
+
598
+ The iOS layer leverages native XCTests integrated directly into CocoaPods via standard development test specs. It verifies FFI path traversal blocking, non-HTTPS download blocks, automatic `deinit` cleanup, and Mach-based telemetry bounds.
599
+
600
+ * **How to run**:
601
+ 1. Boot your preferred iOS simulator (e.g., iPhone 16 running iOS 18.6).
602
+ 2. Run the tests using `xcodebuild`:
603
+ ```bash
604
+ cd example/ios
605
+ xcodebuild test -workspace LLMTest.xcworkspace -scheme react-native-litert-lm-Unit-Tests -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 16'
606
+ ```
607
+
608
+ ### Security & Sanitization Protections Checked
609
+ Every test run automatically asserts:
610
+ - **Defense in depth for download boundaries**: Blocks non-HTTPS schemes at both JS model factory and low-level native layers.
611
+ - **Path Traversal protections**: Prevents directory traversal attacks (`..`, `/`, `\`) in download and deletion APIs.
612
+ - **Telemetry sanity**: Ensures zero-leak memory usage telemetry boundaries stay strictly linear.
613
+
614
+ ## License
615
+
616
+ The code in this repository is licensed under the **[MIT License](LICENSE)**.
617
+
618
+ ### ⚠️ AI Model Disclaimer
619
+
620
+ This library is an execution engine for on-device LLMs. The AI models themselves are **not** distributed with this package and have their own licenses:
621
+
622
+ - **Gemma (Google)**: [Gemma Terms of Use](https://ai.google.dev/gemma/terms)
623
+ - **Llama 3 (Meta)**: [Llama 3.2 Community License](https://www.llama.com/llama3/license/)
624
+ - **Qwen (Alibaba)**: [Apache 2.0](https://huggingface.co/Qwen/Qwen2.5-1.5B-Instruct/blob/main/LICENSE)
625
+ - **Phi (Microsoft)**: [MIT License](https://huggingface.co/microsoft/Phi-3.5-mini-instruct/blob/main/LICENSE)
626
+
627
+ By downloading and using these models, you agree to their respective licenses and acceptable use policies. The author of `react-native-litert-lm` takes no responsibility for model outputs or applications built with them.
@@ -0,0 +1,32 @@
1
+ cmake_minimum_required(VERSION 3.18.0)
2
+ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
3
+
4
+ # Define the library name - must match what Nitrogen expects
5
+ project(LiteRTLM)
6
+
7
+ set(CMAKE_CXX_STANDARD 20)
8
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
9
+
10
+ # Define the shared library (main entry point)
11
+ add_library(
12
+ LiteRTLM
13
+ SHARED
14
+ ../cpp/cpp-adapter.cpp
15
+ # Additional sources are added by autolinking.cmake below
16
+ )
17
+
18
+ # Allow undefined symbols - they will be resolved at runtime when the app
19
+ # loads the NitroModules shared library. This is required because we're
20
+ # building a library that depends on NitroModules symbols which are only
21
+ # available at runtime.
22
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-shlib-undefined,-z,max-page-size=16384")
23
+
24
+ # Include Nitrogen autolinking - this adds all generated sources and links
25
+ include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/LiteRTLM+autolinking.cmake)
26
+
27
+ # Android system libraries
28
+ target_link_libraries(
29
+ LiteRTLM
30
+ android
31
+ log
32
+ )