react-native-litert-lm 0.3.0 → 0.3.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.
package/README.md CHANGED
@@ -448,8 +448,7 @@ const prompt = applyGemmaTemplate(
448
448
  | react-native-nitro-modules | 0.35.0+ |
449
449
  | Android API | 26+ (ARM64) |
450
450
  | iOS | 15.0+ (ARM64) |
451
- | LiteRT-LM Android SDK | 0.9.0-alpha01 |
452
- | LiteRT-LM iOS Engine | v0.9.0 |
451
+ | LiteRT-LM Engine | 0.9.0 |
453
452
 
454
453
  ## Platform Support
455
454
 
@@ -18,6 +18,8 @@
18
18
  #include <chrono>
19
19
  #include <stdexcept>
20
20
  #include <sstream>
21
+ #include <sys/stat.h>
22
+ #include <cstdio>
21
23
 
22
24
  #ifdef __APPLE__
23
25
  #include "IOSDownloadHelper.h"
@@ -241,7 +243,7 @@ void HybridLiteRTLM::loadModelInternal(
241
243
  modelPath.c_str(),
242
244
  backend,
243
245
  visionBackend,
244
- "cpu" // audio always on CPU
246
+ nullptr // audio executor not supported on iOS yet
245
247
  );
246
248
  if (!settings) {
247
249
  return false;
@@ -250,6 +252,10 @@ void HybridLiteRTLM::loadModelInternal(
250
252
  litert_lm_engine_settings_set_max_num_tokens(settings, static_cast<int>(maxTokens_));
251
253
  litert_lm_engine_settings_enable_benchmark(settings);
252
254
 
255
+ // Set cache directory to the same directory as the model file
256
+ std::string cacheDir = modelPath.substr(0, modelPath.find_last_of('/'));
257
+ litert_lm_engine_settings_set_cache_dir(settings, cacheDir.c_str());
258
+
253
259
  engine_ = litert_lm_engine_create(settings);
254
260
  litert_lm_engine_settings_delete(settings);
255
261
 
@@ -275,9 +281,32 @@ void HybridLiteRTLM::loadModelInternal(
275
281
  }
276
282
 
277
283
  if (!engine_) {
284
+ // Collect diagnostic info
285
+ std::string diag = " | Diagnostics: ";
286
+ struct stat st;
287
+ if (stat(modelPath.c_str(), &st) == 0) {
288
+ diag += "File size: " + std::to_string(st.st_size) + " bytes";
289
+ } else {
290
+ diag += "Failed to stat file (errno: " + std::to_string(errno) + ")";
291
+ }
292
+
293
+ FILE* f = fopen(modelPath.c_str(), "rb");
294
+ if (f) {
295
+ diag += ", Readable: YES";
296
+ fclose(f);
297
+ } else {
298
+ diag += ", Readable: NO (errno: " + std::to_string(errno) + ")";
299
+ }
300
+
301
+ // Get the native error from the C API
302
+ const char* nativeErr = litert_lm_get_last_error();
303
+ if (nativeErr && nativeErr[0] != '\0') {
304
+ diag += " | Native error: " + std::string(nativeErr);
305
+ }
306
+
278
307
  throw std::runtime_error(
279
308
  "Failed to create LiteRT-LM engine. Tried backend '" +
280
- std::string(primaryBackend) + "' and CPU fallback. Model path: " + modelPath);
309
+ std::string(primaryBackend) + "' and CPU fallback. Model path: " + modelPath + diag);
281
310
  }
282
311
 
283
312
  session_config_ = litert_lm_session_config_create();
@@ -484,7 +513,12 @@ std::string HybridLiteRTLM::sendMessageWithImageInternal(
484
513
  conversation_, msgJson.c_str(), nullptr);
485
514
 
486
515
  if (!response) {
487
- throw std::runtime_error("LiteRT-LM: sendMessageWithImage failed");
516
+ std::string errMsg = "LiteRT-LM: sendMessageWithImage failed";
517
+ const char* nativeErr = litert_lm_get_last_error();
518
+ if (nativeErr && nativeErr[0] != '\0') {
519
+ errMsg += ": " + std::string(nativeErr);
520
+ }
521
+ throw std::runtime_error(errMsg);
488
522
  }
489
523
 
490
524
  const char* responseStr = litert_lm_json_response_get_string(response);
@@ -232,11 +232,18 @@ LITERT_LM_C_API_EXPORT
232
232
  void litert_lm_engine_settings_set_num_decode_tokens(
233
233
  LiteRtLmEngineSettings* settings, int num_decode_tokens);
234
234
 
235
+ // Returns the last error message from a failed C API call.
236
+ // Returns an empty string if no error has occurred.
237
+ // The returned pointer is valid until the next C API call on the same thread.
238
+ LITERT_LM_C_API_EXPORT
239
+ const char* litert_lm_get_last_error();
240
+
235
241
  // Creates a LiteRT LM Engine from the given settings. The caller is responsible
236
242
  // for destroying the engine using `litert_lm_engine_delete`.
237
243
  //
238
244
  // @param settings The engine settings.
239
245
  // @return A pointer to the created engine, or NULL on failure.
246
+ // Call litert_lm_get_last_error() for details on failure.
240
247
  LITERT_LM_C_API_EXPORT
241
248
  LiteRtLmEngine* litert_lm_engine_create(const LiteRtLmEngineSettings* settings);
242
249
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-litert-lm",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "High-performance LLM inference for React Native using LiteRT-LM. Optimized for Gemma 3n and other on-device language models.",
5
5
  "license": "MIT",
6
6
  "author": "Hugh Chen (https://github.com/hung-yueh)",
@@ -57,6 +57,25 @@ fi
57
57
 
58
58
  LITERT_SRC="$BUILD_DIR/LiteRT-LM"
59
59
 
60
+ # ---- 1b. Apply iOS-specific patches ---------------------------------------
61
+ # These patches fix:
62
+ # - mmap PROT_WRITE removal (iOS rejects CoW for large files)
63
+ # - Error capture API (litert_lm_get_last_error)
64
+ # - Engine registerer moved outside anonymous namespace (iOS linker stripping)
65
+ # - Minijinja/Rust stub replacement (custom C++ prompt template)
66
+ PATCHES_DIR="$PROJECT_ROOT/scripts/patches"
67
+ if [ -d "$PATCHES_DIR" ]; then
68
+ for PATCH_FILE in "$PATCHES_DIR"/*.patch; do
69
+ if [ -f "$PATCH_FILE" ]; then
70
+ echo " Applying patch: $(basename "$PATCH_FILE")..."
71
+ cd "$LITERT_SRC"
72
+ git apply --check "$PATCH_FILE" 2>/dev/null && \
73
+ git apply "$PATCH_FILE" || \
74
+ echo " (patch already applied or conflicts, skipping)"
75
+ fi
76
+ done
77
+ fi
78
+
60
79
  # ---- 2. Verify Bazel is available -----------------------------------------
61
80
  echo ""
62
81
  echo "==> Step 2: Checking Bazel..."