react-native-litert-lm 0.3.6 → 0.4.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.
- package/README.md +207 -158
- package/android/build.gradle +12 -0
- package/android/src/main/AndroidManifest.xml +5 -0
- package/android/src/main/java/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLM.kt +316 -63
- package/android/src/main/java/dev/litert/litertlm/LiteRTLMPackage.kt +19 -2
- package/android/src/test/java/com/margelo/nitro/core/Promise.kt +46 -0
- package/android/src/test/java/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLMTest.kt +83 -0
- package/cpp/include/README.md +9 -11
- package/ios/HybridLiteRTLM.swift +1058 -0
- package/ios/Tests/HybridLiteRTLMTests.swift +67 -0
- package/lib/__mocks__/react-native-nitro-modules.d.ts +61 -0
- package/lib/__mocks__/react-native-nitro-modules.js +50 -0
- package/lib/__tests__/hooks.test.d.ts +1 -0
- package/lib/__tests__/hooks.test.js +124 -0
- package/lib/__tests__/memoryTracker.test.d.ts +1 -0
- package/lib/__tests__/memoryTracker.test.js +74 -0
- package/lib/__tests__/modelFactory.test.d.ts +1 -0
- package/lib/__tests__/modelFactory.test.js +52 -0
- package/lib/hooks.js +1 -1
- package/lib/index.d.ts +2 -4
- package/lib/index.js +12 -7
- package/lib/modelFactory.js +62 -63
- package/lib/specs/LiteRTLM.nitro.d.ts +71 -2
- package/nitrogen/generated/android/c++/JHybridLiteRTLMSpec.cpp +62 -7
- package/nitrogen/generated/android/c++/JHybridLiteRTLMSpec.hpp +3 -1
- package/nitrogen/generated/android/c++/JLLMConfig.hpp +40 -3
- package/nitrogen/generated/android/c++/JMultimodalPart.hpp +74 -0
- package/nitrogen/generated/android/c++/JPartType.hpp +61 -0
- package/nitrogen/generated/android/c++/JToolDefinition.hpp +65 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/GenerationStats.kt +23 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLMSpec.kt +10 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/LLMConfig.kt +46 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/MemoryUsage.kt +19 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/Message.kt +15 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/MultimodalPart.kt +66 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/PartType.kt +24 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/ToolDefinition.kt +61 -0
- package/nitrogen/generated/ios/LiteRTLM-Swift-Cxx-Bridge.cpp +57 -1
- package/nitrogen/generated/ios/LiteRTLM-Swift-Cxx-Bridge.hpp +414 -3
- package/nitrogen/generated/ios/LiteRTLM-Swift-Cxx-Umbrella.hpp +41 -3
- package/nitrogen/generated/ios/LiteRTLMAutolinking.mm +4 -6
- package/nitrogen/generated/ios/LiteRTLMAutolinking.swift +10 -0
- package/nitrogen/generated/ios/c++/HybridLiteRTLMSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridLiteRTLMSpecSwift.hpp +224 -0
- package/nitrogen/generated/ios/swift/Backend.swift +44 -0
- package/nitrogen/generated/ios/swift/Func_void.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_double.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__string.swift +46 -0
- package/nitrogen/generated/ios/swift/Func_void_std__string_bool.swift +46 -0
- package/nitrogen/generated/ios/swift/GenerationStats.swift +54 -0
- package/nitrogen/generated/ios/swift/HybridLiteRTLMSpec.swift +69 -0
- package/nitrogen/generated/ios/swift/HybridLiteRTLMSpec_cxx.swift +383 -0
- package/nitrogen/generated/ios/swift/LLMConfig.swift +203 -0
- package/nitrogen/generated/ios/swift/MemoryUsage.swift +44 -0
- package/nitrogen/generated/ios/swift/Message.swift +34 -0
- package/nitrogen/generated/ios/swift/MultimodalPart.swift +83 -0
- package/nitrogen/generated/ios/swift/PartType.swift +44 -0
- package/nitrogen/generated/ios/swift/Role.swift +44 -0
- package/nitrogen/generated/ios/swift/ToolDefinition.swift +39 -0
- package/nitrogen/generated/shared/c++/HybridLiteRTLMSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridLiteRTLMSpec.hpp +7 -2
- package/nitrogen/generated/shared/c++/LLMConfig.hpp +22 -2
- package/nitrogen/generated/shared/c++/MultimodalPart.hpp +99 -0
- package/nitrogen/generated/shared/c++/PartType.hpp +80 -0
- package/nitrogen/generated/shared/c++/ToolDefinition.hpp +91 -0
- package/package.json +16 -8
- package/react-native-litert-lm.podspec +15 -19
- package/scripts/download-ios-frameworks.sh +14 -48
- package/scripts/postinstall.js +1 -2
- package/src/__mocks__/react-native-nitro-modules.ts +48 -0
- package/src/__tests__/hooks.test.ts +153 -0
- package/src/__tests__/memoryTracker.test.ts +87 -0
- package/src/__tests__/modelFactory.test.ts +68 -0
- package/src/hooks.ts +1 -1
- package/src/index.ts +12 -9
- package/src/modelFactory.ts +82 -80
- package/src/specs/LiteRTLM.nitro.ts +80 -2
- package/cpp/HybridLiteRTLM.cpp +0 -838
- package/cpp/HybridLiteRTLM.hpp +0 -167
- package/cpp/IOSDownloadHelper.h +0 -24
- package/ios/IOSDownloadHelper.mm +0 -129
- package/scripts/build-ios-engine.sh +0 -302
- package/scripts/stubs/cxx_bridge_stubs.cc +0 -224
- package/scripts/stubs/gemma_model_constraint_provider.cc +0 -46
- package/scripts/stubs/llguidance_stubs.c +0 -101
- package/src/templates.ts +0 -105
package/src/modelFactory.ts
CHANGED
|
@@ -54,92 +54,94 @@ export function createLLM(options?: {
|
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
"Insecure HTTP URLs are not allowed for model downloads. " +
|
|
72
|
-
"Use HTTPS instead: " +
|
|
73
|
-
pathOrUrl.replace("http://", "https://"),
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Extract filename from URL
|
|
78
|
-
const fileName = pathOrUrl.split("/").pop();
|
|
79
|
-
if (!fileName) {
|
|
80
|
-
throw new Error(`Invalid model URL: ${pathOrUrl}`);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
console.log(`Checking model at ${pathOrUrl}...`);
|
|
84
|
-
modelPath = await native.downloadModel(
|
|
85
|
-
pathOrUrl,
|
|
86
|
-
fileName,
|
|
87
|
-
(progress) => {
|
|
88
|
-
onDownloadProgress?.(progress);
|
|
89
|
-
},
|
|
57
|
+
const augmentedLoadModel = async (
|
|
58
|
+
pathOrUrl: string,
|
|
59
|
+
config?: LLMConfig,
|
|
60
|
+
onDownloadProgress?: (progress: number) => void,
|
|
61
|
+
) => {
|
|
62
|
+
let modelPath = pathOrUrl;
|
|
63
|
+
|
|
64
|
+
// Check if it's a URL — enforce HTTPS for model downloads
|
|
65
|
+
if (pathOrUrl.startsWith("http://") || pathOrUrl.startsWith("https://")) {
|
|
66
|
+
if (pathOrUrl.startsWith("http://")) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
"Insecure HTTP URLs are not allowed for model downloads. " +
|
|
69
|
+
"Use HTTPS instead: " +
|
|
70
|
+
pathOrUrl.replace("http://", "https://"),
|
|
90
71
|
);
|
|
91
|
-
console.log(`Model downloaded to: ${modelPath}`);
|
|
92
72
|
}
|
|
93
73
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (
|
|
98
|
-
|
|
99
|
-
recordMemorySnapshot();
|
|
74
|
+
// Extract filename from URL, stripping query parameters
|
|
75
|
+
const urlWithoutQuery = pathOrUrl.split("?")[0];
|
|
76
|
+
const fileName = urlWithoutQuery.split("/").pop();
|
|
77
|
+
if (!fileName) {
|
|
78
|
+
throw new Error(`Invalid model URL: ${pathOrUrl}`);
|
|
100
79
|
}
|
|
101
80
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
81
|
+
console.log(`Checking model at ${pathOrUrl}...`);
|
|
82
|
+
modelPath = await native.downloadModel(
|
|
83
|
+
pathOrUrl,
|
|
84
|
+
fileName,
|
|
85
|
+
(progress) => {
|
|
86
|
+
onDownloadProgress?.(progress);
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
console.log(`Model downloaded to: ${modelPath}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const result = await native.loadModel(modelPath, config);
|
|
93
|
+
|
|
94
|
+
// Record initial memory snapshot after model load
|
|
95
|
+
if (tracker) {
|
|
96
|
+
tracker.reset();
|
|
106
97
|
recordMemorySnapshot();
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return result;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const SNAPSHOT_TRIGGERS = new Set([
|
|
104
|
+
"sendMessage",
|
|
105
|
+
"sendMessageWithImage",
|
|
106
|
+
"sendMessageWithAudio",
|
|
107
|
+
"resetConversation",
|
|
108
|
+
]);
|
|
109
|
+
|
|
110
|
+
return new Proxy(native, {
|
|
111
|
+
get(target, prop, receiver) {
|
|
112
|
+
if (prop === "memoryTracker") {
|
|
113
|
+
return tracker;
|
|
114
|
+
}
|
|
115
|
+
if (prop === "loadModel") {
|
|
116
|
+
return augmentedLoadModel;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const original = Reflect.get(target, prop, receiver);
|
|
120
|
+
if (typeof original !== "function") {
|
|
121
|
+
return original;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (prop === "sendMessageAsync") {
|
|
125
|
+
return (message: string, onToken: (token: string, done: boolean) => void) => {
|
|
126
|
+
return original.call(target, message, (token: string, done: boolean) => {
|
|
127
|
+
onToken(token, done);
|
|
128
|
+
if (done) {
|
|
129
|
+
recordMemorySnapshot();
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (SNAPSHOT_TRIGGERS.has(prop as string)) {
|
|
136
|
+
return async (...args: any[]) => {
|
|
137
|
+
const result = await original.apply(target, args);
|
|
114
138
|
recordMemorySnapshot();
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
) => {
|
|
121
|
-
const result = await native.sendMessageWithImage(...args);
|
|
122
|
-
recordMemorySnapshot();
|
|
123
|
-
return result;
|
|
124
|
-
},
|
|
125
|
-
sendMessageWithAudio: async (
|
|
126
|
-
...args: Parameters<typeof native.sendMessageWithAudio>
|
|
127
|
-
) => {
|
|
128
|
-
const result = await native.sendMessageWithAudio(...args);
|
|
129
|
-
recordMemorySnapshot();
|
|
130
|
-
return result;
|
|
131
|
-
},
|
|
132
|
-
getHistory: native.getHistory.bind(native),
|
|
133
|
-
resetConversation: () => {
|
|
134
|
-
native.resetConversation();
|
|
135
|
-
// KV cache is cleared on reset, record the drop
|
|
136
|
-
recordMemorySnapshot();
|
|
139
|
+
return result;
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return original.bind(target);
|
|
137
144
|
},
|
|
138
|
-
|
|
139
|
-
getStats: native.getStats.bind(native),
|
|
140
|
-
getMemoryUsage: native.getMemoryUsage.bind(native),
|
|
141
|
-
close: native.close.bind(native),
|
|
142
|
-
downloadModel: native.downloadModel.bind(native),
|
|
143
|
-
deleteModel: native.deleteModel.bind(native),
|
|
144
|
-
};
|
|
145
|
+
}) as unknown as LiteRTLMInstance;
|
|
145
146
|
}
|
|
147
|
+
|
|
@@ -17,6 +17,37 @@ export type Backend = "cpu" | "gpu" | "npu";
|
|
|
17
17
|
*/
|
|
18
18
|
export type Role = "user" | "model" | "system";
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Definition for a function/tool that the model can request to execute.
|
|
22
|
+
*/
|
|
23
|
+
export interface ToolDefinition {
|
|
24
|
+
/** Name of the function/tool */
|
|
25
|
+
name: string;
|
|
26
|
+
/** Human-readable description of what the function/tool does */
|
|
27
|
+
description: string;
|
|
28
|
+
/** JSON schema defining parameter names and types (stringified) */
|
|
29
|
+
parametersJson: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The part type for a multimodal message content part.
|
|
34
|
+
*/
|
|
35
|
+
export type PartType = "text" | "image" | "audio";
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A part of a unified multimodal message payload.
|
|
39
|
+
*/
|
|
40
|
+
export interface MultimodalPart {
|
|
41
|
+
/** The part type: 'text', 'image', or 'audio' */
|
|
42
|
+
type: PartType;
|
|
43
|
+
/** The plain text content, if type is 'text' */
|
|
44
|
+
text?: string;
|
|
45
|
+
/** Raw image binary data, if type is 'image' (zero-copy ArrayBuffer mapping) */
|
|
46
|
+
imageBuffer?: ArrayBuffer;
|
|
47
|
+
/** Raw audio binary data, if type is 'audio' (zero-copy ArrayBuffer mapping) */
|
|
48
|
+
audioBuffer?: ArrayBuffer;
|
|
49
|
+
}
|
|
50
|
+
|
|
20
51
|
/**
|
|
21
52
|
* Configuration options for loading an LLM.
|
|
22
53
|
*/
|
|
@@ -68,6 +99,41 @@ export interface LLMConfig {
|
|
|
68
99
|
* @default 0.95
|
|
69
100
|
*/
|
|
70
101
|
topP?: number;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Whether to run engine validation after loading the model.
|
|
105
|
+
* When enabled, sends a quick test inference ("Hi") and waits up to 30s
|
|
106
|
+
* for a response to confirm the backend works. This is useful for GPU/NPU
|
|
107
|
+
* backends that may silently fail during inference (they can initialize
|
|
108
|
+
* without error but produce no tokens).
|
|
109
|
+
*
|
|
110
|
+
* Validation is **always a no-op on CPU** — the CPU backend is inherently
|
|
111
|
+
* reliable and never needs validation.
|
|
112
|
+
*
|
|
113
|
+
* Disabled by default because it adds significant latency (5-30s) to model loading.
|
|
114
|
+
* Enable only to catch GPU/NPU silent failure issues during development.
|
|
115
|
+
*
|
|
116
|
+
* @default false
|
|
117
|
+
*/
|
|
118
|
+
validate?: boolean;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Whether this is a multimodal model.
|
|
122
|
+
* When enabled, the engine handles image/audio tokens properly.
|
|
123
|
+
* If not specified, the system will fall back to filename sniffing.
|
|
124
|
+
*/
|
|
125
|
+
multimodal?: boolean;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* List of tools/functions that the model can call.
|
|
129
|
+
*/
|
|
130
|
+
tools?: ToolDefinition[];
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Whether to enable speculative decoding (multi-token prediction) if supported by the model.
|
|
134
|
+
* @default false
|
|
135
|
+
*/
|
|
136
|
+
enableSpeculativeDecoding?: boolean;
|
|
71
137
|
}
|
|
72
138
|
|
|
73
139
|
/**
|
|
@@ -135,7 +201,7 @@ export interface MemoryUsage {
|
|
|
135
201
|
* ```
|
|
136
202
|
*/
|
|
137
203
|
export interface LiteRTLM extends HybridObject<{
|
|
138
|
-
ios: "
|
|
204
|
+
ios: "swift";
|
|
139
205
|
android: "kotlin";
|
|
140
206
|
}> {
|
|
141
207
|
/**
|
|
@@ -187,6 +253,13 @@ export interface LiteRTLM extends HybridObject<{
|
|
|
187
253
|
*/
|
|
188
254
|
sendMessageWithAudio(message: string, audioPath: string): Promise<string>;
|
|
189
255
|
|
|
256
|
+
/**
|
|
257
|
+
* Send a unified multimodal message containing text and/or zero-copy binary buffers.
|
|
258
|
+
* @param parts The message content parts (text, image, and/or audio).
|
|
259
|
+
* @returns The model's response text.
|
|
260
|
+
*/
|
|
261
|
+
sendMultimodalMessage(parts: MultimodalPart[]): Promise<string>;
|
|
262
|
+
|
|
190
263
|
/**
|
|
191
264
|
* Send a message with streaming response.
|
|
192
265
|
* Tokens are delivered via callback as they are generated.
|
|
@@ -196,7 +269,7 @@ export interface LiteRTLM extends HybridObject<{
|
|
|
196
269
|
sendMessageAsync(
|
|
197
270
|
message: string,
|
|
198
271
|
onToken: (token: string, done: boolean) => void,
|
|
199
|
-
): void
|
|
272
|
+
): Promise<void>;
|
|
200
273
|
|
|
201
274
|
/**
|
|
202
275
|
* Get the current conversation history.
|
|
@@ -219,6 +292,11 @@ export interface LiteRTLM extends HybridObject<{
|
|
|
219
292
|
*/
|
|
220
293
|
getStats(): GenerationStats;
|
|
221
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Count tokens in a text string. Returns -1 if unavailable.
|
|
297
|
+
*/
|
|
298
|
+
countTokens(text: string): number;
|
|
299
|
+
|
|
222
300
|
/**
|
|
223
301
|
* Get real memory usage from the native runtime.
|
|
224
302
|
* Uses OS-level APIs to report actual memory consumption.
|