react-native-litert-lm 0.4.0 → 0.4.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/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLM.kt +117 -0
- package/android/src/test/java/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLMTest.kt +22 -0
- package/ios/HybridLiteRTLM.swift +321 -35
- package/ios/Tests/HybridLiteRTLMTests.swift +46 -0
- package/lib/__mocks__/react-native-nitro-modules.d.ts +4 -0
- package/lib/__mocks__/react-native-nitro-modules.js +10 -0
- package/lib/__tests__/modelFactory.test.js +16 -0
- package/lib/hooks.js +27 -3
- package/lib/index.d.ts +6 -0
- package/lib/index.js +7 -3
- package/lib/modelFactory.js +20 -0
- package/lib/specs/LiteRTLM.nitro.d.ts +16 -0
- package/nitrogen/generated/android/LiteRTLMOnLoad.cpp +2 -2
- package/nitrogen/generated/android/c++/JHybridLiteRTLMSpec.cpp +32 -2
- package/nitrogen/generated/android/c++/JHybridLiteRTLMSpec.hpp +2 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/dev/litert/litertlm/HybridLiteRTLMSpec.kt +18 -0
- package/nitrogen/generated/ios/LiteRTLM-Swift-Cxx-Bridge.cpp +8 -8
- package/nitrogen/generated/ios/LiteRTLM-Swift-Cxx-Bridge.hpp +22 -22
- package/nitrogen/generated/ios/c++/HybridLiteRTLMSpecSwift.hpp +16 -0
- package/nitrogen/generated/ios/swift/HybridLiteRTLMSpec.swift +2 -0
- package/nitrogen/generated/ios/swift/HybridLiteRTLMSpec_cxx.swift +48 -0
- package/nitrogen/generated/shared/c++/HybridLiteRTLMSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridLiteRTLMSpec.hpp +2 -0
- package/package.json +7 -4
- package/react-native-litert-lm.podspec +4 -2
- package/scripts/download-ios-frameworks.sh +4 -3
- package/scripts/framework-source.js +46 -0
- package/scripts/postinstall.js +39 -16
- package/src/__mocks__/react-native-nitro-modules.ts +10 -0
- package/src/__tests__/modelFactory.test.ts +28 -0
- package/src/hooks.ts +29 -7
- package/src/index.ts +7 -3
- package/src/modelFactory.ts +22 -0
- package/src/specs/LiteRTLM.nitro.ts +26 -0
package/lib/hooks.js
CHANGED
|
@@ -28,6 +28,11 @@ function useModel(pathOrUrl, config) {
|
|
|
28
28
|
const temperature = config?.temperature;
|
|
29
29
|
const topK = config?.topK;
|
|
30
30
|
const topP = config?.topP;
|
|
31
|
+
const validate = config?.validate;
|
|
32
|
+
const multimodal = config?.multimodal;
|
|
33
|
+
const tools = config?.tools;
|
|
34
|
+
const enableSpeculativeDecoding = config?.enableSpeculativeDecoding;
|
|
35
|
+
const toolsKey = tools ? JSON.stringify(tools) : undefined;
|
|
31
36
|
// Build a stable config object from the destructured primitives
|
|
32
37
|
const nativeConfig = (0, react_1.useMemo)(() => ({
|
|
33
38
|
...(backend !== undefined && { backend }),
|
|
@@ -36,7 +41,24 @@ function useModel(pathOrUrl, config) {
|
|
|
36
41
|
...(temperature !== undefined && { temperature }),
|
|
37
42
|
...(topK !== undefined && { topK }),
|
|
38
43
|
...(topP !== undefined && { topP }),
|
|
39
|
-
|
|
44
|
+
...(validate !== undefined && { validate }),
|
|
45
|
+
...(multimodal !== undefined && { multimodal }),
|
|
46
|
+
...(tools !== undefined && { tools }),
|
|
47
|
+
...(enableSpeculativeDecoding !== undefined && {
|
|
48
|
+
enableSpeculativeDecoding,
|
|
49
|
+
}),
|
|
50
|
+
}), [
|
|
51
|
+
backend,
|
|
52
|
+
systemPrompt,
|
|
53
|
+
maxTokens,
|
|
54
|
+
temperature,
|
|
55
|
+
topK,
|
|
56
|
+
topP,
|
|
57
|
+
validate,
|
|
58
|
+
multimodal,
|
|
59
|
+
toolsKey,
|
|
60
|
+
enableSpeculativeDecoding,
|
|
61
|
+
]);
|
|
40
62
|
/**
|
|
41
63
|
* Refresh memory summary from the tracker's native buffer.
|
|
42
64
|
*/
|
|
@@ -99,13 +121,15 @@ function useModel(pathOrUrl, config) {
|
|
|
99
121
|
return new Promise((resolve, reject) => {
|
|
100
122
|
let fullResponse = "";
|
|
101
123
|
try {
|
|
102
|
-
modelRef.current
|
|
124
|
+
modelRef.current
|
|
125
|
+
?.sendMessageAsync(prompt, (token, done) => {
|
|
103
126
|
fullResponse += token;
|
|
104
127
|
if (done) {
|
|
105
128
|
refreshMemorySummary();
|
|
106
129
|
resolve(fullResponse);
|
|
107
130
|
}
|
|
108
|
-
})
|
|
131
|
+
})
|
|
132
|
+
.catch(reject);
|
|
109
133
|
}
|
|
110
134
|
catch (e) {
|
|
111
135
|
reject(e);
|
package/lib/index.d.ts
CHANGED
|
@@ -93,6 +93,12 @@ export declare function checkBackendSupport(backend: Backend): string | undefine
|
|
|
93
93
|
* Check if multimodal features (image/audio) are supported on the current platform.
|
|
94
94
|
* Returns an error message if not supported, undefined if OK.
|
|
95
95
|
*
|
|
96
|
+
* Both iOS (v0.12.0 CLiteRTLM xcframework) and Android (LiteRT-LM SDK) ship the
|
|
97
|
+
* vision/audio executor ops, so there is no platform-level block. Whether a
|
|
98
|
+
* given call succeeds depends on the **loaded model**: only multimodal models
|
|
99
|
+
* (e.g. Gemma 3n) bundle the vision/audio executors. Pass `multimodal: true` to
|
|
100
|
+
* `loadModel` for such models, or rely on filename sniffing ("3n"/"gemma3").
|
|
101
|
+
*
|
|
96
102
|
* @returns Error message if multimodal is not supported, undefined if OK
|
|
97
103
|
*
|
|
98
104
|
* @example
|
package/lib/index.js
CHANGED
|
@@ -135,6 +135,12 @@ function checkBackendSupport(backend) {
|
|
|
135
135
|
* Check if multimodal features (image/audio) are supported on the current platform.
|
|
136
136
|
* Returns an error message if not supported, undefined if OK.
|
|
137
137
|
*
|
|
138
|
+
* Both iOS (v0.12.0 CLiteRTLM xcframework) and Android (LiteRT-LM SDK) ship the
|
|
139
|
+
* vision/audio executor ops, so there is no platform-level block. Whether a
|
|
140
|
+
* given call succeeds depends on the **loaded model**: only multimodal models
|
|
141
|
+
* (e.g. Gemma 3n) bundle the vision/audio executors. Pass `multimodal: true` to
|
|
142
|
+
* `loadModel` for such models, or rely on filename sniffing ("3n"/"gemma3").
|
|
143
|
+
*
|
|
138
144
|
* @returns Error message if multimodal is not supported, undefined if OK
|
|
139
145
|
*
|
|
140
146
|
* @example
|
|
@@ -149,9 +155,7 @@ function checkBackendSupport(backend) {
|
|
|
149
155
|
* ```
|
|
150
156
|
*/
|
|
151
157
|
function checkMultimodalSupport() {
|
|
152
|
-
|
|
153
|
-
return "Multimodal (image/audio) is not available on iOS. The XCFramework lacks compiled vision and audio executor ops.";
|
|
154
|
-
}
|
|
158
|
+
// Supported on both platforms with a multimodal model loaded.
|
|
155
159
|
return undefined;
|
|
156
160
|
}
|
|
157
161
|
/**
|
package/lib/modelFactory.js
CHANGED
|
@@ -95,6 +95,26 @@ function createLLM(options) {
|
|
|
95
95
|
});
|
|
96
96
|
};
|
|
97
97
|
}
|
|
98
|
+
if (prop === "sendMessageWithImageAsync") {
|
|
99
|
+
return (message, imagePath, onToken) => {
|
|
100
|
+
return original.call(target, message, imagePath, (token, done) => {
|
|
101
|
+
onToken(token, done);
|
|
102
|
+
if (done) {
|
|
103
|
+
recordMemorySnapshot();
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (prop === "sendMessageWithAudioAsync") {
|
|
109
|
+
return (message, audioPath, onToken) => {
|
|
110
|
+
return original.call(target, message, audioPath, (token, done) => {
|
|
111
|
+
onToken(token, done);
|
|
112
|
+
if (done) {
|
|
113
|
+
recordMemorySnapshot();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
}
|
|
98
118
|
if (SNAPSHOT_TRIGGERS.has(prop)) {
|
|
99
119
|
return async (...args) => {
|
|
100
120
|
const result = await original.apply(target, args);
|
|
@@ -204,6 +204,14 @@ export interface LiteRTLM extends HybridObject<{
|
|
|
204
204
|
* @returns The model's response text.
|
|
205
205
|
*/
|
|
206
206
|
sendMessageWithImage(message: string, imagePath: string): Promise<string>;
|
|
207
|
+
/**
|
|
208
|
+
* Send a text message with an image and get a streaming response.
|
|
209
|
+
* Tokens are delivered via callback as they are generated.
|
|
210
|
+
* @param message User message text.
|
|
211
|
+
* @param imagePath Absolute path to an image file.
|
|
212
|
+
* @param onToken Callback invoked for each token (token, isDone).
|
|
213
|
+
*/
|
|
214
|
+
sendMessageWithImageAsync(message: string, imagePath: string, onToken: (token: string, done: boolean) => void): Promise<void>;
|
|
207
215
|
/**
|
|
208
216
|
* Download a model file from a URL.
|
|
209
217
|
* @param url URL to download from.
|
|
@@ -224,6 +232,14 @@ export interface LiteRTLM extends HybridObject<{
|
|
|
224
232
|
* @returns The model's response text.
|
|
225
233
|
*/
|
|
226
234
|
sendMessageWithAudio(message: string, audioPath: string): Promise<string>;
|
|
235
|
+
/**
|
|
236
|
+
* Send a text message with audio and get a streaming response.
|
|
237
|
+
* Tokens are delivered via callback as they are generated.
|
|
238
|
+
* @param message User message text.
|
|
239
|
+
* @param audioPath Absolute path to an audio file (WAV).
|
|
240
|
+
* @param onToken Callback invoked for each token (token, isDone).
|
|
241
|
+
*/
|
|
242
|
+
sendMessageWithAudioAsync(message: string, audioPath: string, onToken: (token: string, done: boolean) => void): Promise<void>;
|
|
227
243
|
/**
|
|
228
244
|
* Send a unified multimodal message containing text and/or zero-copy binary buffers.
|
|
229
245
|
* @param parts The message content parts (text, image, and/or audio).
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
#include <NitroModules/HybridObjectRegistry.hpp>
|
|
17
17
|
|
|
18
18
|
#include "JHybridLiteRTLMSpec.hpp"
|
|
19
|
-
#include "JFunc_void_double.hpp"
|
|
20
19
|
#include "JFunc_void_std__string_bool.hpp"
|
|
20
|
+
#include "JFunc_void_double.hpp"
|
|
21
21
|
#include <NitroModules/DefaultConstructableObject.hpp>
|
|
22
22
|
|
|
23
23
|
namespace margelo::nitro::litertlm {
|
|
@@ -43,8 +43,8 @@ void registerAllNatives() {
|
|
|
43
43
|
|
|
44
44
|
// Register native JNI methods
|
|
45
45
|
margelo::nitro::litertlm::JHybridLiteRTLMSpec::CxxPart::registerNatives();
|
|
46
|
-
margelo::nitro::litertlm::JFunc_void_double_cxx::registerNatives();
|
|
47
46
|
margelo::nitro::litertlm::JFunc_void_std__string_bool_cxx::registerNatives();
|
|
47
|
+
margelo::nitro::litertlm::JFunc_void_double_cxx::registerNatives();
|
|
48
48
|
|
|
49
49
|
// Register Nitro Hybrid Objects
|
|
50
50
|
HybridObjectRegistry::registerHybridObjectConstructor(
|
|
@@ -47,15 +47,15 @@ namespace margelo::nitro::litertlm { enum class PartType; }
|
|
|
47
47
|
#include "ToolDefinition.hpp"
|
|
48
48
|
#include "JToolDefinition.hpp"
|
|
49
49
|
#include <functional>
|
|
50
|
-
#include "
|
|
50
|
+
#include "JFunc_void_std__string_bool.hpp"
|
|
51
51
|
#include <NitroModules/JNICallable.hpp>
|
|
52
|
+
#include "JFunc_void_double.hpp"
|
|
52
53
|
#include "MultimodalPart.hpp"
|
|
53
54
|
#include "JMultimodalPart.hpp"
|
|
54
55
|
#include "PartType.hpp"
|
|
55
56
|
#include "JPartType.hpp"
|
|
56
57
|
#include <NitroModules/ArrayBuffer.hpp>
|
|
57
58
|
#include <NitroModules/JArrayBuffer.hpp>
|
|
58
|
-
#include "JFunc_void_std__string_bool.hpp"
|
|
59
59
|
|
|
60
60
|
namespace margelo::nitro::litertlm {
|
|
61
61
|
|
|
@@ -137,6 +137,21 @@ namespace margelo::nitro::litertlm {
|
|
|
137
137
|
return __promise;
|
|
138
138
|
}();
|
|
139
139
|
}
|
|
140
|
+
std::shared_ptr<Promise<void>> JHybridLiteRTLMSpec::sendMessageWithImageAsync(const std::string& message, const std::string& imagePath, const std::function<void(const std::string& /* token */, bool /* done */)>& onToken) {
|
|
141
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JString> /* message */, jni::alias_ref<jni::JString> /* imagePath */, jni::alias_ref<JFunc_void_std__string_bool::javaobject> /* onToken */)>("sendMessageWithImageAsync_cxx");
|
|
142
|
+
auto __result = method(_javaPart, jni::make_jstring(message), jni::make_jstring(imagePath), JFunc_void_std__string_bool_cxx::fromCpp(onToken));
|
|
143
|
+
return [&]() {
|
|
144
|
+
auto __promise = Promise<void>::create();
|
|
145
|
+
__result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& /* unit */) {
|
|
146
|
+
__promise->resolve();
|
|
147
|
+
});
|
|
148
|
+
__result->cthis()->addOnRejectedListener([=](const jni::alias_ref<jni::JThrowable>& __throwable) {
|
|
149
|
+
jni::JniException __jniError(__throwable);
|
|
150
|
+
__promise->reject(std::make_exception_ptr(__jniError));
|
|
151
|
+
});
|
|
152
|
+
return __promise;
|
|
153
|
+
}();
|
|
154
|
+
}
|
|
140
155
|
std::shared_ptr<Promise<std::string>> JHybridLiteRTLMSpec::downloadModel(const std::string& url, const std::string& fileName, const std::optional<std::function<void(double /* progress */)>>& onProgress) {
|
|
141
156
|
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JString> /* url */, jni::alias_ref<jni::JString> /* fileName */, jni::alias_ref<JFunc_void_double::javaobject> /* onProgress */)>("downloadModel_cxx");
|
|
142
157
|
auto __result = method(_javaPart, jni::make_jstring(url), jni::make_jstring(fileName), onProgress.has_value() ? JFunc_void_double_cxx::fromCpp(onProgress.value()) : nullptr);
|
|
@@ -184,6 +199,21 @@ namespace margelo::nitro::litertlm {
|
|
|
184
199
|
return __promise;
|
|
185
200
|
}();
|
|
186
201
|
}
|
|
202
|
+
std::shared_ptr<Promise<void>> JHybridLiteRTLMSpec::sendMessageWithAudioAsync(const std::string& message, const std::string& audioPath, const std::function<void(const std::string& /* token */, bool /* done */)>& onToken) {
|
|
203
|
+
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JString> /* message */, jni::alias_ref<jni::JString> /* audioPath */, jni::alias_ref<JFunc_void_std__string_bool::javaobject> /* onToken */)>("sendMessageWithAudioAsync_cxx");
|
|
204
|
+
auto __result = method(_javaPart, jni::make_jstring(message), jni::make_jstring(audioPath), JFunc_void_std__string_bool_cxx::fromCpp(onToken));
|
|
205
|
+
return [&]() {
|
|
206
|
+
auto __promise = Promise<void>::create();
|
|
207
|
+
__result->cthis()->addOnResolvedListener([=](const jni::alias_ref<jni::JObject>& /* unit */) {
|
|
208
|
+
__promise->resolve();
|
|
209
|
+
});
|
|
210
|
+
__result->cthis()->addOnRejectedListener([=](const jni::alias_ref<jni::JThrowable>& __throwable) {
|
|
211
|
+
jni::JniException __jniError(__throwable);
|
|
212
|
+
__promise->reject(std::make_exception_ptr(__jniError));
|
|
213
|
+
});
|
|
214
|
+
return __promise;
|
|
215
|
+
}();
|
|
216
|
+
}
|
|
187
217
|
std::shared_ptr<Promise<std::string>> JHybridLiteRTLMSpec::sendMultimodalMessage(const std::vector<MultimodalPart>& parts) {
|
|
188
218
|
static const auto method = _javaPart->javaClassStatic()->getMethod<jni::local_ref<JPromise::javaobject>(jni::alias_ref<jni::JArrayClass<JMultimodalPart>> /* parts */)>("sendMultimodalMessage");
|
|
189
219
|
auto __result = method(_javaPart, [&](auto&& __input) {
|
|
@@ -57,9 +57,11 @@ namespace margelo::nitro::litertlm {
|
|
|
57
57
|
std::shared_ptr<Promise<void>> loadModel(const std::string& modelPath, const std::optional<LLMConfig>& config) override;
|
|
58
58
|
std::shared_ptr<Promise<std::string>> sendMessage(const std::string& message) override;
|
|
59
59
|
std::shared_ptr<Promise<std::string>> sendMessageWithImage(const std::string& message, const std::string& imagePath) override;
|
|
60
|
+
std::shared_ptr<Promise<void>> sendMessageWithImageAsync(const std::string& message, const std::string& imagePath, const std::function<void(const std::string& /* token */, bool /* done */)>& onToken) override;
|
|
60
61
|
std::shared_ptr<Promise<std::string>> downloadModel(const std::string& url, const std::string& fileName, const std::optional<std::function<void(double /* progress */)>>& onProgress) override;
|
|
61
62
|
std::shared_ptr<Promise<void>> deleteModel(const std::string& fileName) override;
|
|
62
63
|
std::shared_ptr<Promise<std::string>> sendMessageWithAudio(const std::string& message, const std::string& audioPath) override;
|
|
64
|
+
std::shared_ptr<Promise<void>> sendMessageWithAudioAsync(const std::string& message, const std::string& audioPath, const std::function<void(const std::string& /* token */, bool /* done */)>& onToken) override;
|
|
63
65
|
std::shared_ptr<Promise<std::string>> sendMultimodalMessage(const std::vector<MultimodalPart>& parts) override;
|
|
64
66
|
std::shared_ptr<Promise<void>> sendMessageAsync(const std::string& message, const std::function<void(const std::string& /* token */, bool /* done */)>& onToken) override;
|
|
65
67
|
std::vector<Message> getHistory() override;
|
|
@@ -41,6 +41,15 @@ abstract class HybridLiteRTLMSpec: HybridObject() {
|
|
|
41
41
|
@Keep
|
|
42
42
|
abstract fun sendMessageWithImage(message: String, imagePath: String): Promise<String>
|
|
43
43
|
|
|
44
|
+
abstract fun sendMessageWithImageAsync(message: String, imagePath: String, onToken: (token: String, done: Boolean) -> Unit): Promise<Unit>
|
|
45
|
+
|
|
46
|
+
@DoNotStrip
|
|
47
|
+
@Keep
|
|
48
|
+
private fun sendMessageWithImageAsync_cxx(message: String, imagePath: String, onToken: Func_void_std__string_bool): Promise<Unit> {
|
|
49
|
+
val __result = sendMessageWithImageAsync(message, imagePath, onToken)
|
|
50
|
+
return __result
|
|
51
|
+
}
|
|
52
|
+
|
|
44
53
|
abstract fun downloadModel(url: String, fileName: String, onProgress: ((progress: Double) -> Unit)?): Promise<String>
|
|
45
54
|
|
|
46
55
|
@DoNotStrip
|
|
@@ -58,6 +67,15 @@ abstract class HybridLiteRTLMSpec: HybridObject() {
|
|
|
58
67
|
@Keep
|
|
59
68
|
abstract fun sendMessageWithAudio(message: String, audioPath: String): Promise<String>
|
|
60
69
|
|
|
70
|
+
abstract fun sendMessageWithAudioAsync(message: String, audioPath: String, onToken: (token: String, done: Boolean) -> Unit): Promise<Unit>
|
|
71
|
+
|
|
72
|
+
@DoNotStrip
|
|
73
|
+
@Keep
|
|
74
|
+
private fun sendMessageWithAudioAsync_cxx(message: String, audioPath: String, onToken: Func_void_std__string_bool): Promise<Unit> {
|
|
75
|
+
val __result = sendMessageWithAudioAsync(message, audioPath, onToken)
|
|
76
|
+
return __result
|
|
77
|
+
}
|
|
78
|
+
|
|
61
79
|
@DoNotStrip
|
|
62
80
|
@Keep
|
|
63
81
|
abstract fun sendMultimodalMessage(parts: Array<MultimodalPart>): Promise<String>
|
|
@@ -38,14 +38,6 @@ namespace margelo::nitro::litertlm::bridge::swift {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
// pragma MARK: std::function<void(double /* progress */)>
|
|
42
|
-
Func_void_double create_Func_void_double(void* NON_NULL swiftClosureWrapper) noexcept {
|
|
43
|
-
auto swiftClosure = LiteRTLM::Func_void_double::fromUnsafe(swiftClosureWrapper);
|
|
44
|
-
return [swiftClosure = std::move(swiftClosure)](double progress) mutable -> void {
|
|
45
|
-
swiftClosure.call(progress);
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
41
|
// pragma MARK: std::function<void(const std::string& /* token */, bool /* done */)>
|
|
50
42
|
Func_void_std__string_bool create_Func_void_std__string_bool(void* NON_NULL swiftClosureWrapper) noexcept {
|
|
51
43
|
auto swiftClosure = LiteRTLM::Func_void_std__string_bool::fromUnsafe(swiftClosureWrapper);
|
|
@@ -54,6 +46,14 @@ namespace margelo::nitro::litertlm::bridge::swift {
|
|
|
54
46
|
};
|
|
55
47
|
}
|
|
56
48
|
|
|
49
|
+
// pragma MARK: std::function<void(double /* progress */)>
|
|
50
|
+
Func_void_double create_Func_void_double(void* NON_NULL swiftClosureWrapper) noexcept {
|
|
51
|
+
auto swiftClosure = LiteRTLM::Func_void_double::fromUnsafe(swiftClosureWrapper);
|
|
52
|
+
return [swiftClosure = std::move(swiftClosure)](double progress) mutable -> void {
|
|
53
|
+
swiftClosure.call(progress);
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
57
|
// pragma MARK: std::shared_ptr<HybridLiteRTLMSpec>
|
|
58
58
|
std::shared_ptr<HybridLiteRTLMSpec> create_std__shared_ptr_HybridLiteRTLMSpec_(void* NON_NULL swiftUnsafePointer) noexcept {
|
|
59
59
|
LiteRTLM::HybridLiteRTLMSpec_cxx swiftPart = LiteRTLM::HybridLiteRTLMSpec_cxx::fromUnsafe(swiftUnsafePointer);
|
|
@@ -255,6 +255,28 @@ namespace margelo::nitro::litertlm::bridge::swift {
|
|
|
255
255
|
return Func_void_std__string_Wrapper(std::move(value));
|
|
256
256
|
}
|
|
257
257
|
|
|
258
|
+
// pragma MARK: std::function<void(const std::string& /* token */, bool /* done */)>
|
|
259
|
+
/**
|
|
260
|
+
* Specialized version of `std::function<void(const std::string&, bool)>`.
|
|
261
|
+
*/
|
|
262
|
+
using Func_void_std__string_bool = std::function<void(const std::string& /* token */, bool /* done */)>;
|
|
263
|
+
/**
|
|
264
|
+
* Wrapper class for a `std::function<void(const std::string& / * token * /, bool / * done * /)>`, this can be used from Swift.
|
|
265
|
+
*/
|
|
266
|
+
class Func_void_std__string_bool_Wrapper final {
|
|
267
|
+
public:
|
|
268
|
+
explicit Func_void_std__string_bool_Wrapper(std::function<void(const std::string& /* token */, bool /* done */)>&& func): _function(std::make_unique<std::function<void(const std::string& /* token */, bool /* done */)>>(std::move(func))) {}
|
|
269
|
+
inline void call(std::string token, bool done) const noexcept {
|
|
270
|
+
_function->operator()(token, done);
|
|
271
|
+
}
|
|
272
|
+
private:
|
|
273
|
+
std::unique_ptr<std::function<void(const std::string& /* token */, bool /* done */)>> _function;
|
|
274
|
+
} SWIFT_NONCOPYABLE;
|
|
275
|
+
Func_void_std__string_bool create_Func_void_std__string_bool(void* NON_NULL swiftClosureWrapper) noexcept;
|
|
276
|
+
inline Func_void_std__string_bool_Wrapper wrap_Func_void_std__string_bool(Func_void_std__string_bool value) noexcept {
|
|
277
|
+
return Func_void_std__string_bool_Wrapper(std::move(value));
|
|
278
|
+
}
|
|
279
|
+
|
|
258
280
|
// pragma MARK: std::function<void(double /* progress */)>
|
|
259
281
|
/**
|
|
260
282
|
* Specialized version of `std::function<void(double)>`.
|
|
@@ -318,28 +340,6 @@ namespace margelo::nitro::litertlm::bridge::swift {
|
|
|
318
340
|
return vector;
|
|
319
341
|
}
|
|
320
342
|
|
|
321
|
-
// pragma MARK: std::function<void(const std::string& /* token */, bool /* done */)>
|
|
322
|
-
/**
|
|
323
|
-
* Specialized version of `std::function<void(const std::string&, bool)>`.
|
|
324
|
-
*/
|
|
325
|
-
using Func_void_std__string_bool = std::function<void(const std::string& /* token */, bool /* done */)>;
|
|
326
|
-
/**
|
|
327
|
-
* Wrapper class for a `std::function<void(const std::string& / * token * /, bool / * done * /)>`, this can be used from Swift.
|
|
328
|
-
*/
|
|
329
|
-
class Func_void_std__string_bool_Wrapper final {
|
|
330
|
-
public:
|
|
331
|
-
explicit Func_void_std__string_bool_Wrapper(std::function<void(const std::string& /* token */, bool /* done */)>&& func): _function(std::make_unique<std::function<void(const std::string& /* token */, bool /* done */)>>(std::move(func))) {}
|
|
332
|
-
inline void call(std::string token, bool done) const noexcept {
|
|
333
|
-
_function->operator()(token, done);
|
|
334
|
-
}
|
|
335
|
-
private:
|
|
336
|
-
std::unique_ptr<std::function<void(const std::string& /* token */, bool /* done */)>> _function;
|
|
337
|
-
} SWIFT_NONCOPYABLE;
|
|
338
|
-
Func_void_std__string_bool create_Func_void_std__string_bool(void* NON_NULL swiftClosureWrapper) noexcept;
|
|
339
|
-
inline Func_void_std__string_bool_Wrapper wrap_Func_void_std__string_bool(Func_void_std__string_bool value) noexcept {
|
|
340
|
-
return Func_void_std__string_bool_Wrapper(std::move(value));
|
|
341
|
-
}
|
|
342
|
-
|
|
343
343
|
// pragma MARK: std::vector<Message>
|
|
344
344
|
/**
|
|
345
345
|
* Specialized version of `std::vector<Message>`.
|
|
@@ -124,6 +124,14 @@ namespace margelo::nitro::litertlm {
|
|
|
124
124
|
auto __value = std::move(__result.value());
|
|
125
125
|
return __value;
|
|
126
126
|
}
|
|
127
|
+
inline std::shared_ptr<Promise<void>> sendMessageWithImageAsync(const std::string& message, const std::string& imagePath, const std::function<void(const std::string& /* token */, bool /* done */)>& onToken) override {
|
|
128
|
+
auto __result = _swiftPart.sendMessageWithImageAsync(message, imagePath, onToken);
|
|
129
|
+
if (__result.hasError()) [[unlikely]] {
|
|
130
|
+
std::rethrow_exception(__result.error());
|
|
131
|
+
}
|
|
132
|
+
auto __value = std::move(__result.value());
|
|
133
|
+
return __value;
|
|
134
|
+
}
|
|
127
135
|
inline std::shared_ptr<Promise<std::string>> downloadModel(const std::string& url, const std::string& fileName, const std::optional<std::function<void(double /* progress */)>>& onProgress) override {
|
|
128
136
|
auto __result = _swiftPart.downloadModel(url, fileName, onProgress);
|
|
129
137
|
if (__result.hasError()) [[unlikely]] {
|
|
@@ -148,6 +156,14 @@ namespace margelo::nitro::litertlm {
|
|
|
148
156
|
auto __value = std::move(__result.value());
|
|
149
157
|
return __value;
|
|
150
158
|
}
|
|
159
|
+
inline std::shared_ptr<Promise<void>> sendMessageWithAudioAsync(const std::string& message, const std::string& audioPath, const std::function<void(const std::string& /* token */, bool /* done */)>& onToken) override {
|
|
160
|
+
auto __result = _swiftPart.sendMessageWithAudioAsync(message, audioPath, onToken);
|
|
161
|
+
if (__result.hasError()) [[unlikely]] {
|
|
162
|
+
std::rethrow_exception(__result.error());
|
|
163
|
+
}
|
|
164
|
+
auto __value = std::move(__result.value());
|
|
165
|
+
return __value;
|
|
166
|
+
}
|
|
151
167
|
inline std::shared_ptr<Promise<std::string>> sendMultimodalMessage(const std::vector<MultimodalPart>& parts) override {
|
|
152
168
|
auto __result = _swiftPart.sendMultimodalMessage(parts);
|
|
153
169
|
if (__result.hasError()) [[unlikely]] {
|
|
@@ -16,9 +16,11 @@ public protocol HybridLiteRTLMSpec_protocol: HybridObject {
|
|
|
16
16
|
func loadModel(modelPath: String, config: LLMConfig?) throws -> Promise<Void>
|
|
17
17
|
func sendMessage(message: String) throws -> Promise<String>
|
|
18
18
|
func sendMessageWithImage(message: String, imagePath: String) throws -> Promise<String>
|
|
19
|
+
func sendMessageWithImageAsync(message: String, imagePath: String, onToken: @escaping (_ token: String, _ done: Bool) -> Void) throws -> Promise<Void>
|
|
19
20
|
func downloadModel(url: String, fileName: String, onProgress: ((_ progress: Double) -> Void)?) throws -> Promise<String>
|
|
20
21
|
func deleteModel(fileName: String) throws -> Promise<Void>
|
|
21
22
|
func sendMessageWithAudio(message: String, audioPath: String) throws -> Promise<String>
|
|
23
|
+
func sendMessageWithAudioAsync(message: String, audioPath: String, onToken: @escaping (_ token: String, _ done: Bool) -> Void) throws -> Promise<Void>
|
|
22
24
|
func sendMultimodalMessage(parts: [MultimodalPart]) throws -> Promise<String>
|
|
23
25
|
func sendMessageAsync(message: String, onToken: @escaping (_ token: String, _ done: Bool) -> Void) throws -> Promise<Void>
|
|
24
26
|
func getHistory() throws -> [Message]
|
|
@@ -181,6 +181,30 @@ open class HybridLiteRTLMSpec_cxx {
|
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
@inline(__always)
|
|
185
|
+
public final func sendMessageWithImageAsync(message: std.string, imagePath: std.string, onToken: bridge.Func_void_std__string_bool) -> bridge.Result_std__shared_ptr_Promise_void___ {
|
|
186
|
+
do {
|
|
187
|
+
let __result = try self.__implementation.sendMessageWithImageAsync(message: String(message), imagePath: String(imagePath), onToken: { () -> (String, Bool) -> Void in
|
|
188
|
+
let __wrappedFunction = bridge.wrap_Func_void_std__string_bool(onToken)
|
|
189
|
+
return { (__token: String, __done: Bool) -> Void in
|
|
190
|
+
__wrappedFunction.call(std.string(__token), __done)
|
|
191
|
+
}
|
|
192
|
+
}())
|
|
193
|
+
let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in
|
|
194
|
+
let __promise = bridge.create_std__shared_ptr_Promise_void__()
|
|
195
|
+
let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise)
|
|
196
|
+
__result
|
|
197
|
+
.then({ __result in __promiseHolder.resolve() })
|
|
198
|
+
.catch({ __error in __promiseHolder.reject(__error.toCpp()) })
|
|
199
|
+
return __promise
|
|
200
|
+
}()
|
|
201
|
+
return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp)
|
|
202
|
+
} catch (let __error) {
|
|
203
|
+
let __exceptionPtr = __error.toCpp()
|
|
204
|
+
return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
184
208
|
@inline(__always)
|
|
185
209
|
public final func downloadModel(url: std.string, fileName: std.string, onProgress: bridge.std__optional_std__function_void_double____progress______) -> bridge.Result_std__shared_ptr_Promise_std__string___ {
|
|
186
210
|
do {
|
|
@@ -250,6 +274,30 @@ open class HybridLiteRTLMSpec_cxx {
|
|
|
250
274
|
}
|
|
251
275
|
}
|
|
252
276
|
|
|
277
|
+
@inline(__always)
|
|
278
|
+
public final func sendMessageWithAudioAsync(message: std.string, audioPath: std.string, onToken: bridge.Func_void_std__string_bool) -> bridge.Result_std__shared_ptr_Promise_void___ {
|
|
279
|
+
do {
|
|
280
|
+
let __result = try self.__implementation.sendMessageWithAudioAsync(message: String(message), audioPath: String(audioPath), onToken: { () -> (String, Bool) -> Void in
|
|
281
|
+
let __wrappedFunction = bridge.wrap_Func_void_std__string_bool(onToken)
|
|
282
|
+
return { (__token: String, __done: Bool) -> Void in
|
|
283
|
+
__wrappedFunction.call(std.string(__token), __done)
|
|
284
|
+
}
|
|
285
|
+
}())
|
|
286
|
+
let __resultCpp = { () -> bridge.std__shared_ptr_Promise_void__ in
|
|
287
|
+
let __promise = bridge.create_std__shared_ptr_Promise_void__()
|
|
288
|
+
let __promiseHolder = bridge.wrap_std__shared_ptr_Promise_void__(__promise)
|
|
289
|
+
__result
|
|
290
|
+
.then({ __result in __promiseHolder.resolve() })
|
|
291
|
+
.catch({ __error in __promiseHolder.reject(__error.toCpp()) })
|
|
292
|
+
return __promise
|
|
293
|
+
}()
|
|
294
|
+
return bridge.create_Result_std__shared_ptr_Promise_void___(__resultCpp)
|
|
295
|
+
} catch (let __error) {
|
|
296
|
+
let __exceptionPtr = __error.toCpp()
|
|
297
|
+
return bridge.create_Result_std__shared_ptr_Promise_void___(__exceptionPtr)
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
253
301
|
@inline(__always)
|
|
254
302
|
public final func sendMultimodalMessage(parts: bridge.std__vector_MultimodalPart_) -> bridge.Result_std__shared_ptr_Promise_std__string___ {
|
|
255
303
|
do {
|
|
@@ -17,9 +17,11 @@ namespace margelo::nitro::litertlm {
|
|
|
17
17
|
prototype.registerHybridMethod("loadModel", &HybridLiteRTLMSpec::loadModel);
|
|
18
18
|
prototype.registerHybridMethod("sendMessage", &HybridLiteRTLMSpec::sendMessage);
|
|
19
19
|
prototype.registerHybridMethod("sendMessageWithImage", &HybridLiteRTLMSpec::sendMessageWithImage);
|
|
20
|
+
prototype.registerHybridMethod("sendMessageWithImageAsync", &HybridLiteRTLMSpec::sendMessageWithImageAsync);
|
|
20
21
|
prototype.registerHybridMethod("downloadModel", &HybridLiteRTLMSpec::downloadModel);
|
|
21
22
|
prototype.registerHybridMethod("deleteModel", &HybridLiteRTLMSpec::deleteModel);
|
|
22
23
|
prototype.registerHybridMethod("sendMessageWithAudio", &HybridLiteRTLMSpec::sendMessageWithAudio);
|
|
24
|
+
prototype.registerHybridMethod("sendMessageWithAudioAsync", &HybridLiteRTLMSpec::sendMessageWithAudioAsync);
|
|
23
25
|
prototype.registerHybridMethod("sendMultimodalMessage", &HybridLiteRTLMSpec::sendMultimodalMessage);
|
|
24
26
|
prototype.registerHybridMethod("sendMessageAsync", &HybridLiteRTLMSpec::sendMessageAsync);
|
|
25
27
|
prototype.registerHybridMethod("getHistory", &HybridLiteRTLMSpec::getHistory);
|
|
@@ -69,9 +69,11 @@ namespace margelo::nitro::litertlm {
|
|
|
69
69
|
virtual std::shared_ptr<Promise<void>> loadModel(const std::string& modelPath, const std::optional<LLMConfig>& config) = 0;
|
|
70
70
|
virtual std::shared_ptr<Promise<std::string>> sendMessage(const std::string& message) = 0;
|
|
71
71
|
virtual std::shared_ptr<Promise<std::string>> sendMessageWithImage(const std::string& message, const std::string& imagePath) = 0;
|
|
72
|
+
virtual std::shared_ptr<Promise<void>> sendMessageWithImageAsync(const std::string& message, const std::string& imagePath, const std::function<void(const std::string& /* token */, bool /* done */)>& onToken) = 0;
|
|
72
73
|
virtual std::shared_ptr<Promise<std::string>> downloadModel(const std::string& url, const std::string& fileName, const std::optional<std::function<void(double /* progress */)>>& onProgress) = 0;
|
|
73
74
|
virtual std::shared_ptr<Promise<void>> deleteModel(const std::string& fileName) = 0;
|
|
74
75
|
virtual std::shared_ptr<Promise<std::string>> sendMessageWithAudio(const std::string& message, const std::string& audioPath) = 0;
|
|
76
|
+
virtual std::shared_ptr<Promise<void>> sendMessageWithAudioAsync(const std::string& message, const std::string& audioPath, const std::function<void(const std::string& /* token */, bool /* done */)>& onToken) = 0;
|
|
75
77
|
virtual std::shared_ptr<Promise<std::string>> sendMultimodalMessage(const std::vector<MultimodalPart>& parts) = 0;
|
|
76
78
|
virtual std::shared_ptr<Promise<void>> sendMessageAsync(const std::string& message, const std::function<void(const std::string& /* token */, bool /* done */)>& onToken) = 0;
|
|
77
79
|
virtual std::vector<Message> getHistory() = 0;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-litert-lm",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"litertLm": {
|
|
5
5
|
"version": "0.12.0",
|
|
6
6
|
"androidMavenVersion": "0.12.0",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"cpp",
|
|
50
50
|
"nitrogen/generated",
|
|
51
51
|
"scripts/postinstall.js",
|
|
52
|
+
"scripts/framework-source.js",
|
|
52
53
|
"scripts/download-ios-frameworks.sh",
|
|
53
54
|
"react-native.config.js",
|
|
54
55
|
"react-native-litert-lm.podspec",
|
|
@@ -65,10 +66,12 @@
|
|
|
65
66
|
"typecheck": "tsc --noEmit",
|
|
66
67
|
"test": "jest",
|
|
67
68
|
"lint": "eslint \"**/*.{js,ts,tsx}\" --fix",
|
|
68
|
-
"prepack": "npm run build",
|
|
69
|
+
"prepack": "npx nitrogen && npm run build",
|
|
69
70
|
"specs": "npx nitrogen",
|
|
70
71
|
"clean": "rm -rf lib android/build ios/build ios/Frameworks nitrogen/generated",
|
|
71
72
|
"download-frameworks": "scripts/download-ios-frameworks.sh",
|
|
73
|
+
"check-framework-release": "node scripts/check-framework-release.js",
|
|
74
|
+
"prepublishOnly": "node scripts/check-framework-release.js",
|
|
72
75
|
"android": "expo run:android",
|
|
73
76
|
"android:clean": "cd android && ./gradlew clean",
|
|
74
77
|
"ios": "expo run:ios",
|
|
@@ -82,7 +85,7 @@
|
|
|
82
85
|
"jest": "^30.4.2",
|
|
83
86
|
"react": "^19.2.6",
|
|
84
87
|
"react-native": "^0.85.3",
|
|
85
|
-
"react-native-nitro-modules": "^0.35.
|
|
88
|
+
"react-native-nitro-modules": "^0.35.9",
|
|
86
89
|
"react-test-renderer": "^19.2.6",
|
|
87
90
|
"release-it": "^19.2.4",
|
|
88
91
|
"ts-jest": "^29.4.10",
|
|
@@ -93,7 +96,7 @@
|
|
|
93
96
|
"expo": ">=55.0.0",
|
|
94
97
|
"react": "*",
|
|
95
98
|
"react-native": "*",
|
|
96
|
-
"react-native-nitro-modules": "^0.35.
|
|
99
|
+
"react-native-nitro-modules": "^0.35.9"
|
|
97
100
|
},
|
|
98
101
|
"peerDependenciesMeta": {
|
|
99
102
|
"expo": {
|
|
@@ -23,8 +23,10 @@ Pod::Spec.new do |s|
|
|
|
23
23
|
"nitrogen/generated/shared/c++/**/*.{hpp,cpp}",
|
|
24
24
|
]
|
|
25
25
|
|
|
26
|
-
# Prebuilt LiteRT-LM C engine (
|
|
27
|
-
#
|
|
26
|
+
# Prebuilt LiteRT-LM C engine (xcframework). Not shipped in the npm tarball;
|
|
27
|
+
# fetched on install by scripts/postinstall.js (asset/tag defined in
|
|
28
|
+
# scripts/framework-source.js). Manual/upstream fallback:
|
|
29
|
+
# scripts/download-ios-frameworks.sh.
|
|
28
30
|
s.vendored_frameworks = 'ios/Frameworks/CLiteRTLM.xcframework'
|
|
29
31
|
|
|
30
32
|
s.pod_target_xcconfig = {
|
|
@@ -7,10 +7,11 @@ set -euo pipefail
|
|
|
7
7
|
|
|
8
8
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
9
9
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
10
|
-
OUTPUT_DIR="$PROJECT_ROOT/ios/Frameworks"
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
# Resolve the asset URL + output dir from the shared single source of truth
|
|
12
|
+
# (scripts/framework-source.js) so this manual path can't drift from postinstall.
|
|
13
|
+
RELEASE_URL="$(node -e "console.log(require('$SCRIPT_DIR/framework-source').ASSET_URL)")"
|
|
14
|
+
OUTPUT_DIR="$(node -e "console.log(require('$SCRIPT_DIR/framework-source').FRAMEWORKS_DIR)")"
|
|
14
15
|
|
|
15
16
|
# Skip if already present
|
|
16
17
|
if [ -d "$OUTPUT_DIR/CLiteRTLM.xcframework" ]; then
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* framework-source.js
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for the prebuilt iOS framework artifact, shared by
|
|
5
|
+
* scripts/postinstall.js, scripts/download-ios-frameworks.sh, and
|
|
6
|
+
* scripts/check-framework-release.js, so the asset name, tag, and URL can never
|
|
7
|
+
* drift between the install path and the release-time guardrail.
|
|
8
|
+
*
|
|
9
|
+
* The artifact is Google's canonical LiteRT-LM release (NOT a per-version
|
|
10
|
+
* re-host of this package), pinned to the LiteRT-LM **engine** version
|
|
11
|
+
* (`litertLm.iosGitTag` in package.json), NOT this wrapper's npm version. So:
|
|
12
|
+
* - patch releases of this wrapper reuse the same framework (no re-upload),
|
|
13
|
+
* - there is no per-release asset for the maintainer to forget to upload,
|
|
14
|
+
* - the guardrail checks the exact URL consumers fetch.
|
|
15
|
+
*
|
|
16
|
+
* Override the host/asset (e.g. to point at a private mirror) via env vars:
|
|
17
|
+
* LITERT_FRAMEWORK_REPO (default: google-ai-edge/LiteRT-LM)
|
|
18
|
+
* LITERT_FRAMEWORK_ASSET (default: CLiteRTLM.xcframework.zip)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const packageJson = require('../package.json');
|
|
23
|
+
|
|
24
|
+
/** GitHub repo that hosts the framework release asset. */
|
|
25
|
+
const GITHUB_REPO = process.env.LITERT_FRAMEWORK_REPO || 'google-ai-edge/LiteRT-LM';
|
|
26
|
+
|
|
27
|
+
/** Release asset filename. Must match the file on the GitHub release. */
|
|
28
|
+
const ASSET_NAME = process.env.LITERT_FRAMEWORK_ASSET || 'CLiteRTLM.xcframework.zip';
|
|
29
|
+
|
|
30
|
+
/** Release tag the asset lives under — the LiteRT-LM engine git tag, e.g. "v0.12.0". */
|
|
31
|
+
const FRAMEWORK_TAG = packageJson.litertLm.iosGitTag;
|
|
32
|
+
|
|
33
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
34
|
+
const FRAMEWORKS_DIR = path.join(PACKAGE_ROOT, 'ios', 'Frameworks');
|
|
35
|
+
|
|
36
|
+
/** Fully-resolved download URL for the framework zip. */
|
|
37
|
+
const ASSET_URL = `https://github.com/${GITHUB_REPO}/releases/download/${FRAMEWORK_TAG}/${ASSET_NAME}`;
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
GITHUB_REPO,
|
|
41
|
+
ASSET_NAME,
|
|
42
|
+
FRAMEWORK_TAG,
|
|
43
|
+
PACKAGE_ROOT,
|
|
44
|
+
FRAMEWORKS_DIR,
|
|
45
|
+
ASSET_URL,
|
|
46
|
+
};
|