react-native-litert-lm 0.3.4 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -15
- package/android/CMakeLists.txt +1 -1
- package/android/src/main/java/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLM.kt +41 -16
- package/cpp/HybridLiteRTLM.cpp +3 -18
- package/cpp/include/litert_lm_engine.h +9 -2
- package/package.json +4 -4
- package/scripts/build-ios-engine.sh +1 -1
- package/scripts/postinstall.js +12 -5
- package/ios/LiteRTLMAutolinking.mm +0 -30
package/README.md
CHANGED
|
@@ -309,11 +309,11 @@ const buffer = tracker.getNativeBuffer();
|
|
|
309
309
|
|
|
310
310
|
Download `.litertlm` models automatically using the exported URL constants, or manually from [HuggingFace](https://huggingface.co/litert-community):
|
|
311
311
|
|
|
312
|
-
| Constant | Model
|
|
313
|
-
| :--------------------- |
|
|
314
|
-
| `GEMMA_4_E2B_IT` | Gemma 4 E2B (Multimodal, IT)
|
|
315
|
-
| `GEMMA_4_E4B_IT` | Gemma 4 E4B (Higher Quality)
|
|
316
|
-
| `GEMMA_3N_E2B_IT_INT4` | Gemma 3n E2B (Int4, Multimodal)
|
|
312
|
+
| Constant | Model | Size | Min RAM | Auth Required |
|
|
313
|
+
| :--------------------- | :------------------------------ | :------ | :------ | :------------- |
|
|
314
|
+
| `GEMMA_4_E2B_IT` | Gemma 4 E2B (Multimodal, IT) | 2.58 GB | 4 GB+ | ❌ No |
|
|
315
|
+
| `GEMMA_4_E4B_IT` | Gemma 4 E4B (Higher Quality) | 3.65 GB | 6 GB+ | ❌ No |
|
|
316
|
+
| `GEMMA_3N_E2B_IT_INT4` | Gemma 3n E2B (Int4, Multimodal) | ~1.3 GB | 4 GB+ | ✅ HuggingFace |
|
|
317
317
|
|
|
318
318
|
> **Recommended:** Use `GEMMA_4_E2B_IT` for most use cases. It's multimodal (text + vision + audio) and downloads directly from HuggingFace without requiring an account.
|
|
319
319
|
>
|
|
@@ -452,7 +452,7 @@ const prompt = applyGemmaTemplate(
|
|
|
452
452
|
| react-native-nitro-modules | 0.35.0+ |
|
|
453
453
|
| Android API | 26+ (ARM64) |
|
|
454
454
|
| iOS | 15.0+ (ARM64) |
|
|
455
|
-
| LiteRT-LM Engine
|
|
455
|
+
| LiteRT-LM Engine | 0.10.2 |
|
|
456
456
|
|
|
457
457
|
## Platform Support
|
|
458
458
|
|
|
@@ -491,7 +491,7 @@ Add to your app's `.entitlements` file:
|
|
|
491
491
|
|
|
492
492
|
## Building the iOS Engine
|
|
493
493
|
|
|
494
|
-
The iOS build uses a **Bazel-to-XCFramework pipeline** that compiles the LiteRT-LM C engine and all transitive dependencies into a static library (~
|
|
494
|
+
The iOS build uses a **Bazel-to-XCFramework pipeline** that compiles the LiteRT-LM C engine and all transitive dependencies into a static library (~82–84 MB).
|
|
495
495
|
|
|
496
496
|
### Prerequisites
|
|
497
497
|
|
|
@@ -506,12 +506,12 @@ The iOS build uses a **Bazel-to-XCFramework pipeline** that compiles the LiteRT-
|
|
|
506
506
|
|
|
507
507
|
This will:
|
|
508
508
|
|
|
509
|
-
1. Clone/checkout LiteRT-LM `v0.10.
|
|
510
|
-
2.
|
|
511
|
-
3.
|
|
512
|
-
4.
|
|
513
|
-
5.
|
|
514
|
-
6. Merge ~1,
|
|
509
|
+
1. Clone/checkout LiteRT-LM `v0.10.2` source into `.litert-lm-build/`
|
|
510
|
+
2. Apply `scripts/patches/ios-engine-fixes.patch` (PromptTemplate simplification, linker fixes)
|
|
511
|
+
3. Build `//c:engine` for `ios_arm64` and `ios_sim_arm64` via Bazel
|
|
512
|
+
4. Collect all transitive `.o` files (engine, protobuf, re2, sentencepiece, etc.)
|
|
513
|
+
5. Compile C/C++ stubs for unavailable Rust dependencies
|
|
514
|
+
6. Merge ~1,909 object files into a static library via `libtool`
|
|
515
515
|
7. Package into `ios/Frameworks/LiteRTLM.xcframework`
|
|
516
516
|
|
|
517
517
|
### Output
|
|
@@ -520,10 +520,10 @@ This will:
|
|
|
520
520
|
ios/Frameworks/LiteRTLM.xcframework/
|
|
521
521
|
├── Info.plist
|
|
522
522
|
├── ios-arm64/LiteRTLM.framework/ # Device
|
|
523
|
-
│ ├── LiteRTLM # ~
|
|
523
|
+
│ ├── LiteRTLM # ~82 MB static library
|
|
524
524
|
│ └── Headers/litert_lm_engine.h
|
|
525
525
|
└── ios-arm64-simulator/LiteRTLM.framework/ # Simulator
|
|
526
|
-
├── LiteRTLM # ~
|
|
526
|
+
├── LiteRTLM # ~84 MB static library
|
|
527
527
|
└── Headers/litert_lm_engine.h
|
|
528
528
|
```
|
|
529
529
|
|
package/android/CMakeLists.txt
CHANGED
|
@@ -19,7 +19,7 @@ add_library(
|
|
|
19
19
|
# loads the NitroModules shared library. This is required because we're
|
|
20
20
|
# building a library that depends on NitroModules symbols which are only
|
|
21
21
|
# available at runtime.
|
|
22
|
-
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-shlib-undefined")
|
|
22
|
+
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--allow-shlib-undefined,-z,max-page-size=16384")
|
|
23
23
|
|
|
24
24
|
# Include Nitrogen autolinking - this adds all generated sources and links
|
|
25
25
|
include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/LiteRTLM+autolinking.cmake)
|
|
@@ -171,27 +171,40 @@ class HybridLiteRTLM : HybridLiteRTLMSpec() {
|
|
|
171
171
|
else -> com.google.ai.edge.litertlm.Backend.CPU()
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
//
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
//
|
|
178
|
-
val
|
|
174
|
+
// Detect multimodal support from model filename.
|
|
175
|
+
// Only Gemma 3n bundles vision/audio executors; Gemma 4 E2B is text-only.
|
|
176
|
+
// Passing vision/audio backends to a text-only model causes
|
|
177
|
+
// vision_litert_compiled_model_executor init failures.
|
|
178
|
+
val modelFileName = modelPath.substringAfterLast("/").lowercase()
|
|
179
|
+
val isMultimodal = modelFileName.contains("3n") || modelFileName.contains("gemma3")
|
|
179
180
|
|
|
180
|
-
|
|
181
|
+
val lmVisionBackend = if (isMultimodal) com.google.ai.edge.litertlm.Backend.GPU() else null
|
|
182
|
+
val lmAudioBackend = if (isMultimodal) com.google.ai.edge.litertlm.Backend.CPU() else null
|
|
183
|
+
|
|
184
|
+
Log.i(TAG, "Backend config: main=$lmBackend, vision=$lmVisionBackend, audio=$lmAudioBackend, multimodal=$isMultimodal")
|
|
181
185
|
|
|
182
186
|
// Get cache directory from application context
|
|
183
187
|
val cacheDirectory = LiteRTLMInitProvider.applicationContext?.cacheDir?.absolutePath
|
|
184
188
|
Log.i(TAG, "Using cache directory: $cacheDirectory")
|
|
185
189
|
|
|
186
|
-
// Create Engine configuration
|
|
187
|
-
val engineConfig =
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
190
|
+
// Create Engine configuration — visionBackend/audioBackend are optional
|
|
191
|
+
val engineConfig = if (isMultimodal) {
|
|
192
|
+
EngineConfig(
|
|
193
|
+
modelPath = modelPath,
|
|
194
|
+
backend = lmBackend,
|
|
195
|
+
visionBackend = lmVisionBackend!!,
|
|
196
|
+
audioBackend = lmAudioBackend!!,
|
|
197
|
+
maxNumTokens = maxTokens,
|
|
198
|
+
cacheDir = cacheDirectory
|
|
199
|
+
)
|
|
200
|
+
} else {
|
|
201
|
+
EngineConfig(
|
|
202
|
+
modelPath = modelPath,
|
|
203
|
+
backend = lmBackend,
|
|
204
|
+
maxNumTokens = maxTokens,
|
|
205
|
+
cacheDir = cacheDirectory
|
|
206
|
+
)
|
|
207
|
+
}
|
|
195
208
|
|
|
196
209
|
if (isClosed) return@synchronized
|
|
197
210
|
|
|
@@ -615,7 +628,19 @@ class HybridLiteRTLM : HybridLiteRTLMSpec() {
|
|
|
615
628
|
|
|
616
629
|
private fun createNewConversation() {
|
|
617
630
|
ensureLoaded()
|
|
618
|
-
//
|
|
631
|
+
// v0.10.2 enforces single-session: close existing conversation first
|
|
632
|
+
conversation?.let { oldConv ->
|
|
633
|
+
try {
|
|
634
|
+
if (oldConv is AutoCloseable) {
|
|
635
|
+
oldConv.close()
|
|
636
|
+
} else {
|
|
637
|
+
oldConv.javaClass.getMethod("close").invoke(oldConv)
|
|
638
|
+
}
|
|
639
|
+
} catch (e: Exception) {
|
|
640
|
+
Log.w(TAG, "Failed to close old conversation: ${e.message}")
|
|
641
|
+
}
|
|
642
|
+
conversation = null
|
|
643
|
+
}
|
|
619
644
|
conversation = engine!!.createConversation()
|
|
620
645
|
// Apply system prompt/instruction if set
|
|
621
646
|
systemPrompt?.let { prompt ->
|
package/cpp/HybridLiteRTLM.cpp
CHANGED
|
@@ -366,12 +366,7 @@ void HybridLiteRTLM::loadModelInternal(
|
|
|
366
366
|
} else {
|
|
367
367
|
diag += ", Readable: NO (errno: " + std::to_string(errno) + ")";
|
|
368
368
|
}
|
|
369
|
-
|
|
370
|
-
// Get the native error from the C API
|
|
371
|
-
const char* nativeErr = litert_lm_get_last_error();
|
|
372
|
-
if (nativeErr && nativeErr[0] != '\0') {
|
|
373
|
-
diag += " | Native error: " + std::string(nativeErr);
|
|
374
|
-
}
|
|
369
|
+
|
|
375
370
|
|
|
376
371
|
throw std::runtime_error(
|
|
377
372
|
"Failed to create LiteRT-LM engine. Tried backend '" +
|
|
@@ -602,12 +597,7 @@ std::string HybridLiteRTLM::sendMessageWithImageInternal(
|
|
|
602
597
|
conversation_, msgJson.c_str(), nullptr);
|
|
603
598
|
|
|
604
599
|
if (!response) {
|
|
605
|
-
std::
|
|
606
|
-
const char* nativeErr = litert_lm_get_last_error();
|
|
607
|
-
if (nativeErr && nativeErr[0] != '\0') {
|
|
608
|
-
errMsg += ": " + std::string(nativeErr);
|
|
609
|
-
}
|
|
610
|
-
throw std::runtime_error(errMsg);
|
|
600
|
+
throw std::runtime_error("LiteRT-LM: sendMessageWithImage failed");
|
|
611
601
|
}
|
|
612
602
|
|
|
613
603
|
const char* responseStr = litert_lm_json_response_get_string(response);
|
|
@@ -667,12 +657,7 @@ std::string HybridLiteRTLM::sendMessageWithAudioInternal(
|
|
|
667
657
|
conversation_, msgJson.c_str(), nullptr);
|
|
668
658
|
|
|
669
659
|
if (!response) {
|
|
670
|
-
std::
|
|
671
|
-
const char* nativeErr = litert_lm_get_last_error();
|
|
672
|
-
if (nativeErr && nativeErr[0] != '\0') {
|
|
673
|
-
errMsg += ": " + std::string(nativeErr);
|
|
674
|
-
}
|
|
675
|
-
throw std::runtime_error(errMsg);
|
|
660
|
+
throw std::runtime_error("LiteRT-LM: sendMessageWithAudio failed");
|
|
676
661
|
}
|
|
677
662
|
|
|
678
663
|
const char* responseStr = litert_lm_json_response_get_string(response);
|
|
@@ -182,6 +182,15 @@ LITERT_LM_C_API_EXPORT
|
|
|
182
182
|
void litert_lm_engine_settings_set_max_num_tokens(
|
|
183
183
|
LiteRtLmEngineSettings* settings, int max_num_tokens);
|
|
184
184
|
|
|
185
|
+
// Sets whether the engine should load different sections of the litertlm file
|
|
186
|
+
// in parallel. Defaults to true.
|
|
187
|
+
//
|
|
188
|
+
// @param settings The engine settings.
|
|
189
|
+
// @param parallel_file_section_loading Whether to load in parallel.
|
|
190
|
+
LITERT_LM_C_API_EXPORT
|
|
191
|
+
void litert_lm_engine_settings_set_parallel_file_section_loading(
|
|
192
|
+
LiteRtLmEngineSettings* settings, bool parallel_file_section_loading);
|
|
193
|
+
|
|
185
194
|
// Sets the cache directory for the engine.
|
|
186
195
|
//
|
|
187
196
|
// @param settings The engine settings.
|
|
@@ -236,14 +245,12 @@ void litert_lm_engine_settings_set_num_decode_tokens(
|
|
|
236
245
|
// Returns an empty string if no error has occurred.
|
|
237
246
|
// The returned pointer is valid until the next C API call on the same thread.
|
|
238
247
|
LITERT_LM_C_API_EXPORT
|
|
239
|
-
const char* litert_lm_get_last_error();
|
|
240
248
|
|
|
241
249
|
// Creates a LiteRT LM Engine from the given settings. The caller is responsible
|
|
242
250
|
// for destroying the engine using `litert_lm_engine_delete`.
|
|
243
251
|
//
|
|
244
252
|
// @param settings The engine settings.
|
|
245
253
|
// @return A pointer to the created engine, or NULL on failure.
|
|
246
|
-
// Call litert_lm_get_last_error() for details on failure.
|
|
247
254
|
LITERT_LM_C_API_EXPORT
|
|
248
255
|
LiteRtLmEngine* litert_lm_engine_create(const LiteRtLmEngineSettings* settings);
|
|
249
256
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-litert-lm",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"litertLm": {
|
|
5
|
-
"version": "0.10.
|
|
6
|
-
"androidMavenVersion": "0.10.
|
|
7
|
-
"iosGitTag": "v0.10.
|
|
5
|
+
"version": "0.10.2",
|
|
6
|
+
"androidMavenVersion": "0.10.2",
|
|
7
|
+
"iosGitTag": "v0.10.2"
|
|
8
8
|
},
|
|
9
9
|
"description": "High-performance LLM inference for React Native using LiteRT-LM. Optimized for Gemma 4 and other on-device language models.",
|
|
10
10
|
"license": "MIT",
|
|
@@ -267,7 +267,7 @@ for ARCH_NAME in "device" "simulator"; do
|
|
|
267
267
|
<key>CFBundlePackageType</key>
|
|
268
268
|
<string>FMWK</string>
|
|
269
269
|
<key>CFBundleShortVersionString</key>
|
|
270
|
-
<string>0.
|
|
270
|
+
<string>0.10.2</string>
|
|
271
271
|
<key>CFBundleVersion</key>
|
|
272
272
|
<string>1</string>
|
|
273
273
|
<key>MinimumOSVersion</key>
|
package/scripts/postinstall.js
CHANGED
|
@@ -103,13 +103,20 @@ async function main() {
|
|
|
103
103
|
|
|
104
104
|
log('iOS frameworks installed successfully.');
|
|
105
105
|
} catch (err) {
|
|
106
|
-
// Don't fail the install — iOS frameworks are optional (Android-only users)
|
|
107
|
-
log(`Warning: Could not download iOS frameworks: ${err.message}`);
|
|
108
|
-
log('iOS builds will not work until frameworks are available.');
|
|
109
|
-
log('Run: scripts/download-ios-frameworks.sh to download manually.');
|
|
110
|
-
|
|
111
106
|
// Cleanup partial download
|
|
112
107
|
try { fs.unlinkSync(tmpZip); } catch {}
|
|
108
|
+
|
|
109
|
+
log(`Error: Could not download iOS frameworks: ${err.message}`);
|
|
110
|
+
log('iOS builds will not work until frameworks are available.');
|
|
111
|
+
log('Run: ./scripts/download-ios-frameworks.sh to download manually,');
|
|
112
|
+
log(' or: ./scripts/build-ios-engine.sh to build from source.');
|
|
113
|
+
|
|
114
|
+
// Fail fast on macOS so users discover the problem now, not at Xcode link time.
|
|
115
|
+
// Skip SKIP_IOS_FRAMEWORK_DOWNLOAD is already checked above.
|
|
116
|
+
if (process.platform === 'darwin') {
|
|
117
|
+
log('Set SKIP_IOS_FRAMEWORK_DOWNLOAD=1 to suppress this error (e.g. Android-only builds).');
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
113
120
|
}
|
|
114
121
|
}
|
|
115
122
|
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
///
|
|
2
|
-
/// LiteRTLMAutolinking.mm
|
|
3
|
-
/// Registers the C++ HybridLiteRTLM implementation with NitroModules on iOS.
|
|
4
|
-
///
|
|
5
|
-
/// On iOS, there's no JNI_OnLoad equivalent, so we use ObjC +load to register
|
|
6
|
-
/// the HybridObject factory before JS tries to create it.
|
|
7
|
-
///
|
|
8
|
-
|
|
9
|
-
#import <Foundation/Foundation.h>
|
|
10
|
-
#include <NitroModules/HybridObjectRegistry.hpp>
|
|
11
|
-
#include "HybridLiteRTLM.hpp"
|
|
12
|
-
|
|
13
|
-
@interface LiteRTLMAutolinking : NSObject
|
|
14
|
-
@end
|
|
15
|
-
|
|
16
|
-
@implementation LiteRTLMAutolinking
|
|
17
|
-
|
|
18
|
-
+ (void)load {
|
|
19
|
-
using namespace margelo::nitro;
|
|
20
|
-
using namespace margelo::nitro::litertlm;
|
|
21
|
-
|
|
22
|
-
HybridObjectRegistry::registerHybridObjectConstructor(
|
|
23
|
-
"LiteRTLM",
|
|
24
|
-
[]() -> std::shared_ptr<HybridObject> {
|
|
25
|
-
return std::make_shared<HybridLiteRTLM>();
|
|
26
|
-
}
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
@end
|