react-native-litert-lm 0.1.1 → 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.
Files changed (31) hide show
  1. package/README.md +149 -31
  2. package/android/src/main/java/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLM.kt +307 -61
  3. package/cpp/HybridLiteRTLM.cpp +85 -31
  4. package/cpp/HybridLiteRTLM.hpp +4 -0
  5. package/cpp/include/stb_image.h +7988 -0
  6. package/lib/hooks.d.ts +16 -0
  7. package/lib/hooks.js +114 -0
  8. package/lib/index.d.ts +27 -2
  9. package/lib/index.js +50 -6
  10. package/lib/modelFactory.d.ts +5 -0
  11. package/lib/modelFactory.js +42 -0
  12. package/lib/specs/LiteRTLM.nitro.d.ts +19 -0
  13. package/lib/templates.d.ts +51 -0
  14. package/lib/templates.js +81 -0
  15. package/nitrogen/generated/android/LiteRTLMOnLoad.cpp +2 -0
  16. package/nitrogen/generated/android/c++/JFunc_void_double.hpp +75 -0
  17. package/nitrogen/generated/android/c++/JHybridLiteRTLMSpec.cpp +33 -1
  18. package/nitrogen/generated/android/c++/JHybridLiteRTLMSpec.hpp +2 -0
  19. package/nitrogen/generated/android/c++/JLLMConfig.hpp +6 -1
  20. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/Func_void_double.kt +80 -0
  21. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLMSpec.kt +13 -0
  22. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/LLMConfig.kt +5 -2
  23. package/nitrogen/generated/shared/c++/HybridLiteRTLMSpec.cpp +2 -0
  24. package/nitrogen/generated/shared/c++/HybridLiteRTLMSpec.hpp +2 -0
  25. package/nitrogen/generated/shared/c++/LLMConfig.hpp +7 -2
  26. package/package.json +1 -1
  27. package/src/hooks.ts +152 -0
  28. package/src/index.ts +41 -3
  29. package/src/modelFactory.ts +49 -0
  30. package/src/specs/LiteRTLM.nitro.ts +26 -0
  31. package/src/templates.ts +105 -0
@@ -11,9 +11,13 @@
11
11
 
12
12
  #include "HybridLiteRTLM.hpp"
13
13
 
14
+ #define STB_IMAGE_IMPLEMENTATION
15
+ #include "include/stb_image.h"
16
+
14
17
  #include <chrono>
15
18
  #include <stdexcept>
16
19
  #include <sstream>
20
+ #include <fstream>
17
21
 
18
22
  namespace margelo::nitro::litertlm {
19
23
 
@@ -229,32 +233,46 @@ std::string HybridLiteRTLM::sendMessageWithImage(
229
233
  ensureLoaded();
230
234
 
231
235
  #ifdef LITERT_LM_ENABLED
232
- // TODO: Load image file into raw pixel buffer
233
- // The Engine expects raw RGBA/RGB data, not a file path.
234
- // Implementation should:
235
- // 1. Read image file (using stb_image.h or Android Bitmap JNI)
236
- // 2. Decode to raw pixel buffer (std::vector<uint8_t>)
237
- // 3. Create litert::lm::ImageData or equivalent tensor
238
- // 4. Pass to conversation_->SendMessage with multimodal content
239
-
240
- // For now, fall back to text-only with a note about the image
241
- std::string augmentedMessage = message + " [Image attached: " + imagePath +
242
- " - Note: Image processing not yet implemented, text-only response]";
243
-
236
+ // Load image using stb_image
237
+ int width, height, channels;
238
+ unsigned char* img = stbi_load(imagePath.c_str(), &width, &height, &channels, 3); // Force 3 channels (RGB)
239
+ if (img == nullptr) {
240
+ throw std::runtime_error("Failed to load image from path: " + imagePath);
241
+ }
242
+
243
+ // Create input tensor/buffer for the engine.
244
+ // Note: The exact API for passing image data depends on the LiteRT-LM version.
245
+ // Assuming a structure that accepts raw bytes and dimensions.
244
246
  litert::lm::UserMessage lm_message;
245
247
  lm_message.role = "user";
246
- lm_message.content = augmentedMessage;
247
248
 
249
+ // Construct multimodal content
250
+ // Option A: If UserMessage supports a list of content parts
251
+ litert::lm::ContentPart textPart;
252
+ textPart.type = litert::lm::ContentType::TEXT;
253
+ textPart.text = message;
254
+ lm_message.parts.push_back(textPart);
255
+
256
+ litert::lm::ContentPart imagePart;
257
+ imagePart.type = litert::lm::ContentType::IMAGE;
258
+ imagePart.image.width = width;
259
+ imagePart.image.height = height;
260
+ imagePart.image.channels = channels;
261
+ imagePart.image.data = std::vector<uint8_t>(img, img + (width * height * channels));
262
+ lm_message.parts.push_back(imagePart);
263
+
264
+ stbi_image_free(img);
265
+
248
266
  auto response = conversation_->SendMessage(lm_message);
249
267
  if (!response.ok()) {
250
268
  throw std::runtime_error("Multimodal inference failed: " +
251
269
  std::string(response.status().message()));
252
270
  }
253
271
 
254
- // Add to history
272
+ // Add to history (metadata only)
255
273
  Message userMessage;
256
274
  userMessage.role = Role::USER;
257
- userMessage.content = message + " [with image]";
275
+ userMessage.content = message + " [Image]";
258
276
  history_.push_back(userMessage);
259
277
 
260
278
  Message modelMessage;
@@ -265,11 +283,34 @@ std::string HybridLiteRTLM::sendMessageWithImage(
265
283
  return response->content;
266
284
 
267
285
  #else
268
- // Stub: just process text with image path noted
269
- return sendMessage(message + " [Image: " + imagePath + "]");
286
+ // iOS: LiteRT-LM SDK not yet available, throw clear error
287
+ throw std::runtime_error(
288
+ "sendMessageWithImage is not supported on iOS. "
289
+ "LiteRT-LM iOS SDK is not yet available. "
290
+ "Please use text-only sendMessage() for now.");
291
+ #endif
292
+ }
293
+
270
294
  #endif
271
295
  }
272
296
 
297
+ //------------------------------------------------------------------------------
298
+ // downloadModel - Download model file from URL
299
+ //------------------------------------------------------------------------------
300
+ std::future<std::string> HybridLiteRTLM::downloadModel(
301
+ const std::string& url,
302
+ const std::string& fileName,
303
+ const std::optional<std::function<void(double)>>& onProgress) {
304
+
305
+ // Return a future that throws an exception
306
+ return std::async(std::launch::async, []() -> std::string {
307
+ throw std::runtime_error(
308
+ "downloadModel is not supported on iOS yet. "
309
+ "Please download the model manually using a separate library."
310
+ );
311
+ });
312
+ }
313
+
273
314
  //------------------------------------------------------------------------------
274
315
  // sendMessageWithAudio - Multimodal audio + text
275
316
  //------------------------------------------------------------------------------
@@ -281,31 +322,41 @@ std::string HybridLiteRTLM::sendMessageWithAudio(
281
322
  ensureLoaded();
282
323
 
283
324
  #ifdef LITERT_LM_ENABLED
284
- // TODO: Load audio file into raw sample buffer
285
- // Similar to image - Engine expects raw audio samples, not file path.
286
- // Implementation should:
287
- // 1. Read WAV file header and samples
288
- // 2. Convert to expected format (likely 16kHz mono float32)
289
- // 3. Create litert::lm::AudioData or equivalent
290
- // 4. Pass to conversation with multimodal content
325
+ // Load audio file
326
+ std::ifstream audioFile(audioPath, std::ios::binary);
327
+ if (!audioFile) {
328
+ throw std::runtime_error("Failed to open audio file: " + audioPath);
329
+ }
291
330
 
292
- std::string augmentedMessage = message + " [Audio attached: " + audioPath +
293
- " - Note: Audio processing not yet implemented, text-only response]";
331
+ // Simple WAV header skip (simplistic, assuming standard header size for now or raw)
332
+ // Ideally use a WAV parsing library or miniaudio if available.
333
+ // For this implementation, we read the whole file.
334
+ std::vector<uint8_t> audioData((std::istreambuf_iterator<char>(audioFile)), std::istreambuf_iterator<char>());
294
335
 
295
336
  litert::lm::UserMessage lm_message;
296
337
  lm_message.role = "user";
297
- lm_message.content = augmentedMessage;
298
338
 
339
+ litert::lm::ContentPart textPart;
340
+ textPart.type = litert::lm::ContentType::TEXT;
341
+ textPart.text = message;
342
+ lm_message.parts.push_back(textPart);
343
+
344
+ litert::lm::ContentPart audioPart;
345
+ audioPart.type = litert::lm::ContentType::AUDIO;
346
+ audioPart.audio.data = audioData;
347
+ // Metadata like sample rate might be needed:
348
+ // audioPart.audio.sample_rate = 16000;
349
+ lm_message.parts.push_back(audioPart);
350
+
299
351
  auto response = conversation_->SendMessage(lm_message);
300
352
  if (!response.ok()) {
301
353
  throw std::runtime_error("Audio inference failed: " +
302
354
  std::string(response.status().message()));
303
355
  }
304
356
 
305
- // Add to history
306
357
  Message userMessage;
307
358
  userMessage.role = Role::USER;
308
- userMessage.content = message + " [with audio]";
359
+ userMessage.content = message + " [Audio]";
309
360
  history_.push_back(userMessage);
310
361
 
311
362
  Message modelMessage;
@@ -316,8 +367,11 @@ std::string HybridLiteRTLM::sendMessageWithAudio(
316
367
  return response->content;
317
368
 
318
369
  #else
319
- // Stub: just process text with audio path noted
320
- return sendMessage(message + " [Audio: " + audioPath + "]");
370
+ // iOS: LiteRT-LM SDK not yet available, throw clear error
371
+ throw std::runtime_error(
372
+ "sendMessageWithAudio is not supported on iOS. "
373
+ "LiteRT-LM iOS SDK is not yet available. "
374
+ "Please use text-only sendMessage() for now.");
321
375
  #endif
322
376
  }
323
377
 
@@ -58,6 +58,10 @@ public:
58
58
 
59
59
  std::string sendMessageWithImage(const std::string& message,
60
60
  const std::string& imagePath) override;
61
+
62
+ std::future<std::string> downloadModel(const std::string& url,
63
+ const std::string& fileName,
64
+ const std::optional<std::function<void(double)>>& onProgress) override;
61
65
 
62
66
  std::string sendMessageWithAudio(const std::string& message,
63
67
  const std::string& audioPath) override;