react-native-litert-lm 0.2.0 → 0.2.2

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 (38) hide show
  1. package/README.md +245 -29
  2. package/android/src/main/java/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLM.kt +301 -58
  3. package/cpp/HybridLiteRTLM.cpp +109 -9
  4. package/cpp/HybridLiteRTLM.hpp +16 -0
  5. package/cpp/cpp-adapter.cpp +10 -2
  6. package/lib/hooks.d.ts +41 -0
  7. package/lib/hooks.js +131 -0
  8. package/lib/index.d.ts +30 -3
  9. package/lib/index.js +53 -6
  10. package/lib/memoryTracker.d.ts +128 -0
  11. package/lib/memoryTracker.js +155 -0
  12. package/lib/modelFactory.d.ts +18 -0
  13. package/lib/modelFactory.js +104 -0
  14. package/lib/specs/LiteRTLM.nitro.d.ts +38 -0
  15. package/lib/templates.d.ts +51 -0
  16. package/lib/templates.js +81 -0
  17. package/nitrogen/generated/android/LiteRTLMOnLoad.cpp +22 -17
  18. package/nitrogen/generated/android/LiteRTLMOnLoad.hpp +13 -4
  19. package/nitrogen/generated/android/c++/JFunc_void_double.hpp +75 -0
  20. package/nitrogen/generated/android/c++/JHybridLiteRTLMSpec.cpp +42 -1
  21. package/nitrogen/generated/android/c++/JHybridLiteRTLMSpec.hpp +3 -0
  22. package/nitrogen/generated/android/c++/JLLMConfig.hpp +6 -1
  23. package/nitrogen/generated/android/c++/JMemoryUsage.hpp +69 -0
  24. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/Func_void_double.kt +80 -0
  25. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLMSpec.kt +17 -0
  26. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/LLMConfig.kt +5 -2
  27. package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/MemoryUsage.kt +47 -0
  28. package/nitrogen/generated/shared/c++/HybridLiteRTLMSpec.cpp +3 -0
  29. package/nitrogen/generated/shared/c++/HybridLiteRTLMSpec.hpp +6 -0
  30. package/nitrogen/generated/shared/c++/LLMConfig.hpp +7 -2
  31. package/nitrogen/generated/shared/c++/MemoryUsage.hpp +95 -0
  32. package/package.json +3 -3
  33. package/src/hooks.ts +195 -0
  34. package/src/index.ts +51 -3
  35. package/src/memoryTracker.ts +268 -0
  36. package/src/modelFactory.ts +120 -0
  37. package/src/specs/LiteRTLM.nitro.ts +47 -0
  38. package/src/templates.ts +105 -0
package/lib/hooks.js ADDED
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useModel = useModel;
4
+ const react_1 = require("react");
5
+ const modelFactory_1 = require("./modelFactory");
6
+ function useModel(pathOrUrl, config) {
7
+ const modelRef = (0, react_1.useRef)(null);
8
+ const [isReady, setIsReady] = (0, react_1.useState)(false);
9
+ const [isGenerating, setIsGenerating] = (0, react_1.useState)(false);
10
+ const [downloadProgress, setDownloadProgress] = (0, react_1.useState)(0);
11
+ const [error, setError] = (0, react_1.useState)(null);
12
+ const [memorySummary, setMemorySummary] = (0, react_1.useState)(null);
13
+ // Extract autoLoad (default true) and memory tracking options
14
+ const autoLoad = config?.autoLoad ?? true;
15
+ const enableMemoryTracking = config?.enableMemoryTracking ?? false;
16
+ const maxMemorySnapshots = config?.maxMemorySnapshots ?? 256;
17
+ /**
18
+ * Refresh memory summary from the tracker's native buffer.
19
+ */
20
+ const refreshMemorySummary = (0, react_1.useCallback)(() => {
21
+ if (modelRef.current?.memoryTracker) {
22
+ setMemorySummary(modelRef.current.memoryTracker.getSummary());
23
+ }
24
+ }, []);
25
+ // Initialize the model instance
26
+ (0, react_1.useEffect)(() => {
27
+ modelRef.current = (0, modelFactory_1.createLLM)({
28
+ enableMemoryTracking,
29
+ maxMemorySnapshots,
30
+ });
31
+ let isMounted = true;
32
+ // Cleanup on unmount
33
+ return () => {
34
+ isMounted = false;
35
+ try {
36
+ modelRef.current?.close();
37
+ }
38
+ catch (e) {
39
+ console.warn("Failed to close model", e);
40
+ }
41
+ };
42
+ }, [enableMemoryTracking, maxMemorySnapshots]);
43
+ const load = (0, react_1.useCallback)(async () => {
44
+ setIsReady(false);
45
+ setError(null);
46
+ setDownloadProgress(0);
47
+ try {
48
+ let modelPath = pathOrUrl;
49
+ // Handle URL download manually to capture progress
50
+ if (pathOrUrl.startsWith("http://") || pathOrUrl.startsWith("https://")) {
51
+ const fileName = pathOrUrl.split("/").pop() || "model.bin";
52
+ if (modelRef.current) {
53
+ modelPath = await modelRef.current.downloadModel(pathOrUrl, fileName, (progress) => {
54
+ setDownloadProgress(progress);
55
+ });
56
+ }
57
+ }
58
+ if (modelRef.current) {
59
+ // Create a clean config object for native loadModel (excluding autoLoad)
60
+ const nativeConfig = { ...config };
61
+ delete nativeConfig.autoLoad;
62
+ await modelRef.current.loadModel(modelPath, nativeConfig);
63
+ setIsReady(true);
64
+ }
65
+ }
66
+ catch (e) {
67
+ setError(e.message || "Failed to load model");
68
+ console.error(e);
69
+ }
70
+ }, [pathOrUrl, config]);
71
+ (0, react_1.useEffect)(() => {
72
+ if (autoLoad) {
73
+ load();
74
+ }
75
+ }, [autoLoad, load]);
76
+ const generate = (0, react_1.useCallback)(async (prompt) => {
77
+ if (!modelRef.current || !isReady) {
78
+ throw new Error("Model not ready");
79
+ }
80
+ setIsGenerating(true);
81
+ try {
82
+ return new Promise((resolve, reject) => {
83
+ let fullResponse = "";
84
+ try {
85
+ modelRef.current?.sendMessageAsync(prompt, (token, done) => {
86
+ fullResponse += token;
87
+ if (done) {
88
+ refreshMemorySummary();
89
+ resolve(fullResponse);
90
+ }
91
+ });
92
+ }
93
+ catch (e) {
94
+ reject(e);
95
+ }
96
+ });
97
+ }
98
+ catch (e) {
99
+ setError(e.message || "Generation failed");
100
+ throw e;
101
+ }
102
+ finally {
103
+ setIsGenerating(false);
104
+ }
105
+ }, [isReady, refreshMemorySummary]);
106
+ const reset = (0, react_1.useCallback)(() => {
107
+ if (modelRef.current) {
108
+ modelRef.current.resetConversation();
109
+ }
110
+ }, []);
111
+ const deleteModel = (0, react_1.useCallback)(async (fileName) => {
112
+ if (modelRef.current) {
113
+ await modelRef.current.deleteModel(fileName);
114
+ setIsReady(false);
115
+ setDownloadProgress(0);
116
+ }
117
+ }, []);
118
+ return {
119
+ model: modelRef.current,
120
+ isReady,
121
+ isGenerating,
122
+ downloadProgress,
123
+ error,
124
+ generate,
125
+ reset,
126
+ deleteModel,
127
+ load,
128
+ memoryTracker: modelRef.current?.memoryTracker ?? null,
129
+ memorySummary,
130
+ };
131
+ }
package/lib/index.d.ts CHANGED
@@ -1,5 +1,10 @@
1
- import type { LiteRTLM, Backend } from "./specs/LiteRTLM.nitro";
2
- export type { LiteRTLM, LLMConfig, Message, Backend, Role, GenerationStats, } from "./specs/LiteRTLM.nitro";
1
+ import type { Backend } from "./specs/LiteRTLM.nitro";
2
+ export type { LiteRTLM, LLMConfig, Message, Backend, Role, GenerationStats, MemoryUsage, } from "./specs/LiteRTLM.nitro";
3
+ export type { ChatMessage } from "./templates";
4
+ export { applyGemmaTemplate, applyPhiTemplate, applyLlamaTemplate, } from "./templates";
5
+ export type { MemorySnapshot, MemoryTracker, MemoryTrackerSummary, } from "./memoryTracker";
6
+ export { createMemoryTracker, createNativeBuffer } from "./memoryTracker";
7
+ export * from "./hooks";
3
8
  /**
4
9
  * Creates a new LiteRT-LM inference engine instance.
5
10
  *
@@ -33,7 +38,7 @@ export type { LiteRTLM, LLMConfig, Message, Backend, Role, GenerationStats, } fr
33
38
  * llm.close();
34
39
  * ```
35
40
  */
36
- export declare function createLLM(): LiteRTLM;
41
+ export { createLLM } from "./modelFactory";
37
42
  /**
38
43
  * Pre-defined model identifiers for common models.
39
44
  * Use with model download utilities or as reference.
@@ -80,3 +85,25 @@ export declare function getRecommendedBackend(): Backend;
80
85
  * ```
81
86
  */
82
87
  export declare function checkBackendSupport(backend: Backend): string | undefined;
88
+ /**
89
+ * Check if multimodal features (image/audio) are supported on the current platform.
90
+ * Returns an error message if not supported, undefined if OK.
91
+ *
92
+ * @returns Error message if multimodal is not supported, undefined if OK
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * const error = checkMultimodalSupport();
97
+ * if (error) {
98
+ * console.warn(error);
99
+ * // Fall back to text-only
100
+ * } else {
101
+ * llm.sendMessageWithImage('Describe this', imagePath);
102
+ * }
103
+ * ```
104
+ */
105
+ export declare function checkMultimodalSupport(): string | undefined;
106
+ /**
107
+ * Download URL for the Gemma 3n E2B IT INT4 model.
108
+ */
109
+ export declare const GEMMA_3N_E2B_IT_INT4 = "https://litert.dev/gemma-3n-E2B-it-int4.litertlm";
package/lib/index.js CHANGED
@@ -1,11 +1,32 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Models = void 0;
4
- exports.createLLM = createLLM;
17
+ exports.GEMMA_3N_E2B_IT_INT4 = exports.Models = exports.createLLM = exports.createNativeBuffer = exports.createMemoryTracker = exports.applyLlamaTemplate = exports.applyPhiTemplate = exports.applyGemmaTemplate = void 0;
5
18
  exports.getRecommendedBackend = getRecommendedBackend;
6
19
  exports.checkBackendSupport = checkBackendSupport;
7
- const react_native_nitro_modules_1 = require("react-native-nitro-modules");
20
+ exports.checkMultimodalSupport = checkMultimodalSupport;
8
21
  const react_native_1 = require("react-native");
22
+ var templates_1 = require("./templates");
23
+ Object.defineProperty(exports, "applyGemmaTemplate", { enumerable: true, get: function () { return templates_1.applyGemmaTemplate; } });
24
+ Object.defineProperty(exports, "applyPhiTemplate", { enumerable: true, get: function () { return templates_1.applyPhiTemplate; } });
25
+ Object.defineProperty(exports, "applyLlamaTemplate", { enumerable: true, get: function () { return templates_1.applyLlamaTemplate; } });
26
+ var memoryTracker_1 = require("./memoryTracker");
27
+ Object.defineProperty(exports, "createMemoryTracker", { enumerable: true, get: function () { return memoryTracker_1.createMemoryTracker; } });
28
+ Object.defineProperty(exports, "createNativeBuffer", { enumerable: true, get: function () { return memoryTracker_1.createNativeBuffer; } });
29
+ __exportStar(require("./hooks"), exports);
9
30
  /**
10
31
  * Creates a new LiteRT-LM inference engine instance.
11
32
  *
@@ -39,9 +60,8 @@ const react_native_1 = require("react-native");
39
60
  * llm.close();
40
61
  * ```
41
62
  */
42
- function createLLM() {
43
- return react_native_nitro_modules_1.NitroModules.createHybridObject("LiteRTLM");
44
- }
63
+ var modelFactory_1 = require("./modelFactory");
64
+ Object.defineProperty(exports, "createLLM", { enumerable: true, get: function () { return modelFactory_1.createLLM; } });
45
65
  /**
46
66
  * Pre-defined model identifiers for common models.
47
67
  * Use with model download utilities or as reference.
@@ -104,3 +124,30 @@ function checkBackendSupport(backend) {
104
124
  }
105
125
  return undefined;
106
126
  }
127
+ /**
128
+ * Check if multimodal features (image/audio) are supported on the current platform.
129
+ * Returns an error message if not supported, undefined if OK.
130
+ *
131
+ * @returns Error message if multimodal is not supported, undefined if OK
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * const error = checkMultimodalSupport();
136
+ * if (error) {
137
+ * console.warn(error);
138
+ * // Fall back to text-only
139
+ * } else {
140
+ * llm.sendMessageWithImage('Describe this', imagePath);
141
+ * }
142
+ * ```
143
+ */
144
+ function checkMultimodalSupport() {
145
+ if (react_native_1.Platform.OS === "ios") {
146
+ return "Multimodal (image/audio) is not yet supported on iOS. LiteRT-LM iOS SDK is pending.";
147
+ }
148
+ return undefined;
149
+ }
150
+ /**
151
+ * Download URL for the Gemma 3n E2B IT INT4 model.
152
+ */
153
+ exports.GEMMA_3N_E2B_IT_INT4 = "https://litert.dev/gemma-3n-E2B-it-int4.litertlm";
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Memory tracking utilities for LiteRT-LM using real native memory metrics.
3
+ *
4
+ * Records real memory usage from OS-level APIs via `getMemoryUsage()`,
5
+ * and stores snapshots in a native-backed ArrayBuffer allocated via
6
+ * `NitroModules.createNativeArrayBuffer()` (v0.34+) for zero-copy interop.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { createMemoryTracker } from 'react-native-litert-lm';
11
+ *
12
+ * const tracker = createMemoryTracker(100);
13
+ *
14
+ * // Record a real snapshot (typically called internally after inference)
15
+ * tracker.record({
16
+ * timestamp: Date.now(),
17
+ * nativeHeapBytes: usage.nativeHeapBytes,
18
+ * residentBytes: usage.residentBytes,
19
+ * availableMemoryBytes: usage.availableMemoryBytes,
20
+ * });
21
+ *
22
+ * console.log(`Peak RSS: ${tracker.getPeakMemory()} bytes`);
23
+ * ```
24
+ */
25
+ /**
26
+ * A single memory usage snapshot with real data from OS APIs.
27
+ */
28
+ export interface MemorySnapshot {
29
+ /** Unix timestamp in milliseconds */
30
+ timestamp: number;
31
+ /** Native heap allocated bytes (Debug.getNativeHeapAllocatedSize on Android, task_info on iOS) */
32
+ nativeHeapBytes: number;
33
+ /** Process resident set size (RSS) in bytes */
34
+ residentBytes: number;
35
+ /** Available system memory in bytes */
36
+ availableMemoryBytes: number;
37
+ }
38
+ /**
39
+ * Memory tracker that stores snapshots in a native-backed ArrayBuffer.
40
+ *
41
+ * Uses `NitroModules.createNativeArrayBuffer()` to allocate the backing
42
+ * buffer in native (C++) memory, ensuring zero-copy interop with native
43
+ * methods and keeping memory tracking data off the JS heap.
44
+ */
45
+ export interface MemoryTracker {
46
+ /**
47
+ * Record a new memory snapshot.
48
+ * @param snapshot The memory usage data to record
49
+ * @returns true if recorded, false if buffer is full
50
+ */
51
+ record(snapshot: MemorySnapshot): boolean;
52
+ /**
53
+ * Get all recorded snapshots as structured objects.
54
+ */
55
+ getSnapshots(): MemorySnapshot[];
56
+ /**
57
+ * Get the number of recorded snapshots.
58
+ */
59
+ getSnapshotCount(): number;
60
+ /**
61
+ * Get the maximum number of snapshots this tracker can hold.
62
+ */
63
+ getCapacity(): number;
64
+ /**
65
+ * Get the peak resident set size across all snapshots.
66
+ */
67
+ getPeakMemory(): number;
68
+ /**
69
+ * Get the latest memory snapshot, or undefined if none recorded.
70
+ */
71
+ getLatestSnapshot(): MemorySnapshot | undefined;
72
+ /**
73
+ * Get the underlying native ArrayBuffer.
74
+ * This buffer is allocated via `NitroModules.createNativeArrayBuffer()`
75
+ * and lives in native memory, enabling zero-copy transfer to native methods.
76
+ */
77
+ getNativeBuffer(): ArrayBuffer;
78
+ /**
79
+ * Get the Float64Array view over the native buffer.
80
+ */
81
+ getView(): Float64Array;
82
+ /**
83
+ * Reset the tracker, clearing all recorded snapshots.
84
+ * The native buffer is preserved (not reallocated).
85
+ */
86
+ reset(): void;
87
+ /**
88
+ * Get a summary of memory usage statistics.
89
+ */
90
+ getSummary(): MemoryTrackerSummary;
91
+ }
92
+ /**
93
+ * Summary statistics from the memory tracker.
94
+ */
95
+ export interface MemoryTrackerSummary {
96
+ /** Number of snapshots recorded */
97
+ snapshotCount: number;
98
+ /** Peak resident set size in bytes */
99
+ peakResidentBytes: number;
100
+ /** Average resident set size in bytes */
101
+ averageResidentBytes: number;
102
+ /** Latest resident set size in bytes */
103
+ currentResidentBytes: number;
104
+ /** Peak native heap allocated in bytes */
105
+ peakNativeHeapBytes: number;
106
+ /** Latest native heap allocated in bytes */
107
+ currentNativeHeapBytes: number;
108
+ /** RSS delta from first to last snapshot in bytes */
109
+ residentDeltaBytes: number;
110
+ /** Size of the native tracking buffer itself in bytes */
111
+ trackerBufferSizeBytes: number;
112
+ }
113
+ /**
114
+ * Create a new memory tracker backed by a native ArrayBuffer.
115
+ *
116
+ * @param maxSnapshots Maximum number of snapshots to store (default: 256)
117
+ * @returns A MemoryTracker instance
118
+ */
119
+ export declare function createMemoryTracker(maxSnapshots?: number): MemoryTracker;
120
+ /**
121
+ * Create a native ArrayBuffer for efficient data transfer.
122
+ *
123
+ * A convenience wrapper around `NitroModules.createNativeArrayBuffer()`.
124
+ *
125
+ * @param size Size in bytes
126
+ * @returns A native-backed ArrayBuffer
127
+ */
128
+ export declare function createNativeBuffer(size: number): ArrayBuffer;
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ /**
3
+ * Memory tracking utilities for LiteRT-LM using real native memory metrics.
4
+ *
5
+ * Records real memory usage from OS-level APIs via `getMemoryUsage()`,
6
+ * and stores snapshots in a native-backed ArrayBuffer allocated via
7
+ * `NitroModules.createNativeArrayBuffer()` (v0.34+) for zero-copy interop.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { createMemoryTracker } from 'react-native-litert-lm';
12
+ *
13
+ * const tracker = createMemoryTracker(100);
14
+ *
15
+ * // Record a real snapshot (typically called internally after inference)
16
+ * tracker.record({
17
+ * timestamp: Date.now(),
18
+ * nativeHeapBytes: usage.nativeHeapBytes,
19
+ * residentBytes: usage.residentBytes,
20
+ * availableMemoryBytes: usage.availableMemoryBytes,
21
+ * });
22
+ *
23
+ * console.log(`Peak RSS: ${tracker.getPeakMemory()} bytes`);
24
+ * ```
25
+ */
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ exports.createMemoryTracker = createMemoryTracker;
28
+ exports.createNativeBuffer = createNativeBuffer;
29
+ const react_native_nitro_modules_1 = require("react-native-nitro-modules");
30
+ /** Number of Float64 fields per snapshot */
31
+ const FIELDS_PER_SNAPSHOT = 4;
32
+ /** Bytes per Float64 value */
33
+ const BYTES_PER_FIELD = Float64Array.BYTES_PER_ELEMENT; // 8
34
+ /**
35
+ * Create a new memory tracker backed by a native ArrayBuffer.
36
+ *
37
+ * @param maxSnapshots Maximum number of snapshots to store (default: 256)
38
+ * @returns A MemoryTracker instance
39
+ */
40
+ function createMemoryTracker(maxSnapshots = 256) {
41
+ const bufferSize = maxSnapshots * FIELDS_PER_SNAPSHOT * BYTES_PER_FIELD;
42
+ // Use NitroModules.createNativeArrayBuffer for native-backed allocation.
43
+ const nativeBuffer = react_native_nitro_modules_1.NitroModules.createNativeArrayBuffer(bufferSize);
44
+ const view = new Float64Array(nativeBuffer);
45
+ let currentIndex = 0;
46
+ return {
47
+ record(snapshot) {
48
+ if (currentIndex >= maxSnapshots) {
49
+ return false;
50
+ }
51
+ const offset = currentIndex * FIELDS_PER_SNAPSHOT;
52
+ view[offset] = snapshot.timestamp;
53
+ view[offset + 1] = snapshot.nativeHeapBytes;
54
+ view[offset + 2] = snapshot.residentBytes;
55
+ view[offset + 3] = snapshot.availableMemoryBytes;
56
+ currentIndex++;
57
+ return true;
58
+ },
59
+ getSnapshots() {
60
+ const snapshots = [];
61
+ for (let i = 0; i < currentIndex; i++) {
62
+ const offset = i * FIELDS_PER_SNAPSHOT;
63
+ snapshots.push({
64
+ timestamp: view[offset],
65
+ nativeHeapBytes: view[offset + 1],
66
+ residentBytes: view[offset + 2],
67
+ availableMemoryBytes: view[offset + 3],
68
+ });
69
+ }
70
+ return snapshots;
71
+ },
72
+ getSnapshotCount() {
73
+ return currentIndex;
74
+ },
75
+ getCapacity() {
76
+ return maxSnapshots;
77
+ },
78
+ getPeakMemory() {
79
+ let peak = 0;
80
+ for (let i = 0; i < currentIndex; i++) {
81
+ const rss = view[i * FIELDS_PER_SNAPSHOT + 2];
82
+ if (rss > peak) {
83
+ peak = rss;
84
+ }
85
+ }
86
+ return peak;
87
+ },
88
+ getLatestSnapshot() {
89
+ if (currentIndex === 0)
90
+ return undefined;
91
+ const offset = (currentIndex - 1) * FIELDS_PER_SNAPSHOT;
92
+ return {
93
+ timestamp: view[offset],
94
+ nativeHeapBytes: view[offset + 1],
95
+ residentBytes: view[offset + 2],
96
+ availableMemoryBytes: view[offset + 3],
97
+ };
98
+ },
99
+ getNativeBuffer() {
100
+ return nativeBuffer;
101
+ },
102
+ getView() {
103
+ return view;
104
+ },
105
+ reset() {
106
+ view.fill(0);
107
+ currentIndex = 0;
108
+ },
109
+ getSummary() {
110
+ let peakRss = 0;
111
+ let peakHeap = 0;
112
+ let sumRss = 0;
113
+ let firstRss = 0;
114
+ let lastRss = 0;
115
+ let lastHeap = 0;
116
+ for (let i = 0; i < currentIndex; i++) {
117
+ const offset = i * FIELDS_PER_SNAPSHOT;
118
+ const heap = view[offset + 1];
119
+ const rss = view[offset + 2];
120
+ if (rss > peakRss)
121
+ peakRss = rss;
122
+ if (heap > peakHeap)
123
+ peakHeap = heap;
124
+ sumRss += rss;
125
+ if (i === 0)
126
+ firstRss = rss;
127
+ if (i === currentIndex - 1) {
128
+ lastRss = rss;
129
+ lastHeap = heap;
130
+ }
131
+ }
132
+ return {
133
+ snapshotCount: currentIndex,
134
+ peakResidentBytes: peakRss,
135
+ averageResidentBytes: currentIndex > 0 ? sumRss / currentIndex : 0,
136
+ currentResidentBytes: lastRss,
137
+ peakNativeHeapBytes: peakHeap,
138
+ currentNativeHeapBytes: lastHeap,
139
+ residentDeltaBytes: lastRss - firstRss,
140
+ trackerBufferSizeBytes: bufferSize,
141
+ };
142
+ },
143
+ };
144
+ }
145
+ /**
146
+ * Create a native ArrayBuffer for efficient data transfer.
147
+ *
148
+ * A convenience wrapper around `NitroModules.createNativeArrayBuffer()`.
149
+ *
150
+ * @param size Size in bytes
151
+ * @returns A native-backed ArrayBuffer
152
+ */
153
+ function createNativeBuffer(size) {
154
+ return react_native_nitro_modules_1.NitroModules.createNativeArrayBuffer(size);
155
+ }
@@ -0,0 +1,18 @@
1
+ import { LiteRTLM } from "./specs/LiteRTLM.nitro";
2
+ import { MemoryTracker } from "./memoryTracker";
3
+ /**
4
+ * Creates a new LiteRT-LM inference engine instance.
5
+ *
6
+ * Optionally creates a native-backed memory tracker using
7
+ * `NitroModules.createNativeArrayBuffer()` (v0.34+) for efficient
8
+ * zero-copy memory usage tracking.
9
+ *
10
+ * @param options.enableMemoryTracking Enable automatic memory tracking (default: false)
11
+ * @param options.maxMemorySnapshots Maximum number of memory snapshots to store (default: 256)
12
+ */
13
+ export declare function createLLM(options?: {
14
+ enableMemoryTracking?: boolean;
15
+ maxMemorySnapshots?: number;
16
+ }): LiteRTLM & {
17
+ memoryTracker?: MemoryTracker;
18
+ };
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createLLM = createLLM;
4
+ const react_native_nitro_modules_1 = require("react-native-nitro-modules");
5
+ const memoryTracker_1 = require("./memoryTracker");
6
+ /**
7
+ * Creates a new LiteRT-LM inference engine instance.
8
+ *
9
+ * Optionally creates a native-backed memory tracker using
10
+ * `NitroModules.createNativeArrayBuffer()` (v0.34+) for efficient
11
+ * zero-copy memory usage tracking.
12
+ *
13
+ * @param options.enableMemoryTracking Enable automatic memory tracking (default: false)
14
+ * @param options.maxMemorySnapshots Maximum number of memory snapshots to store (default: 256)
15
+ */
16
+ function createLLM(options) {
17
+ const native = react_native_nitro_modules_1.NitroModules.createHybridObject("LiteRTLM");
18
+ const enableTracking = options?.enableMemoryTracking ?? false;
19
+ const tracker = enableTracking
20
+ ? (0, memoryTracker_1.createMemoryTracker)(options?.maxMemorySnapshots ?? 256)
21
+ : undefined;
22
+ /**
23
+ * Record a real memory snapshot using OS-level APIs via getMemoryUsage().
24
+ */
25
+ const recordMemorySnapshot = () => {
26
+ if (!tracker)
27
+ return;
28
+ try {
29
+ const usage = native.getMemoryUsage();
30
+ tracker.record({
31
+ timestamp: Date.now(),
32
+ nativeHeapBytes: usage.nativeHeapBytes,
33
+ residentBytes: usage.residentBytes,
34
+ availableMemoryBytes: usage.availableMemoryBytes,
35
+ });
36
+ }
37
+ catch {
38
+ // Ignore errors during memory tracking - it's non-critical
39
+ }
40
+ };
41
+ return {
42
+ ...native,
43
+ memoryTracker: tracker,
44
+ loadModel: async (pathOrUrl, config) => {
45
+ let modelPath = pathOrUrl;
46
+ // Check if it's a URL
47
+ if (pathOrUrl.startsWith("http://") || pathOrUrl.startsWith("https://")) {
48
+ // Extract filename from URL
49
+ const fileName = pathOrUrl.split("/").pop();
50
+ if (!fileName) {
51
+ throw new Error(`Invalid model URL: ${pathOrUrl}`);
52
+ }
53
+ console.log(`Checking model at ${pathOrUrl}...`);
54
+ modelPath = await native.downloadModel(pathOrUrl, fileName, (progress) => {
55
+ console.log(`Download progress: ${progress}`);
56
+ });
57
+ console.log(`Model downloaded to: ${modelPath}`);
58
+ }
59
+ const result = await native.loadModel(modelPath, config);
60
+ // Record initial memory snapshot after model load
61
+ if (tracker) {
62
+ tracker.reset();
63
+ recordMemorySnapshot();
64
+ }
65
+ return result;
66
+ },
67
+ sendMessage: async (...args) => {
68
+ const result = await native.sendMessage(...args);
69
+ recordMemorySnapshot();
70
+ return result;
71
+ },
72
+ sendMessageAsync: (...args) => {
73
+ const [message, onToken] = args;
74
+ native.sendMessageAsync(message, (token, done) => {
75
+ onToken(token, done);
76
+ if (done) {
77
+ recordMemorySnapshot();
78
+ }
79
+ });
80
+ },
81
+ sendMessageWithImage: async (...args) => {
82
+ const result = await native.sendMessageWithImage(...args);
83
+ recordMemorySnapshot();
84
+ return result;
85
+ },
86
+ sendMessageWithAudio: async (...args) => {
87
+ const result = await native.sendMessageWithAudio(...args);
88
+ recordMemorySnapshot();
89
+ return result;
90
+ },
91
+ getHistory: native.getHistory.bind(native),
92
+ resetConversation: () => {
93
+ native.resetConversation();
94
+ // KV cache is cleared on reset, record the drop
95
+ recordMemorySnapshot();
96
+ },
97
+ isReady: native.isReady.bind(native),
98
+ getStats: native.getStats.bind(native),
99
+ getMemoryUsage: native.getMemoryUsage.bind(native),
100
+ close: native.close.bind(native),
101
+ downloadModel: native.downloadModel.bind(native),
102
+ deleteModel: native.deleteModel.bind(native),
103
+ };
104
+ }