react-native-executorch 0.10.2 → 0.10.3
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/build.gradle.kts +26 -2
- package/cpp/extensions/llm/llm_runner.cpp +142 -12
- package/cpp/extensions/llm/llm_runner.h +7 -0
- package/lib/module/extensions/llm/llmRunner.js.map +1 -1
- package/lib/module/extensions/llm/tasks/llmChatSession.js +4 -4
- package/lib/module/extensions/llm/tasks/llmChatSession.js.map +1 -1
- package/lib/module/extensions/llm/utils/tokenizerConfig.js +3 -1
- package/lib/module/extensions/llm/utils/tokenizerConfig.js.map +1 -1
- package/lib/module/fetcher/telemetry.js +1 -1
- package/lib/typescript/src/extensions/llm/llmRunner.d.ts +0 -2
- package/lib/typescript/src/extensions/llm/llmRunner.d.ts.map +1 -1
- package/lib/typescript/src/extensions/llm/utils/tokenizerConfig.d.ts +4 -0
- package/lib/typescript/src/extensions/llm/utils/tokenizerConfig.d.ts.map +1 -1
- package/package.json +2 -2
- package/scripts/download-libs.js +139 -11
- package/src/extensions/llm/llmRunner.ts +0 -2
- package/src/extensions/llm/tasks/llmChatSession.ts +5 -5
- package/src/extensions/llm/utils/tokenizerConfig.ts +11 -1
- package/src/fetcher/telemetry.ts +1 -1
package/android/build.gradle.kts
CHANGED
|
@@ -144,8 +144,13 @@ android {
|
|
|
144
144
|
getByName("main") {
|
|
145
145
|
// Prebuilt executorch + backend .so live under
|
|
146
146
|
// third-party/android/libs/executorch/<abi>/. Pointing jniLibs here
|
|
147
|
-
// makes Gradle package them
|
|
148
|
-
//
|
|
147
|
+
// makes Gradle package them into the APK alongside our own .so.
|
|
148
|
+
//
|
|
149
|
+
// Gradle packages every .so it finds under a jniLibs source dir, so
|
|
150
|
+
// this alone ships backends the app opted out of: -DRNE_ENABLE_* only
|
|
151
|
+
// stops CMake importing and linking them, which is enough for
|
|
152
|
+
// behaviour but has no bearing on packaging. `packaging.jniLibs
|
|
153
|
+
// .excludes` below drops the disabled ones back out.
|
|
149
154
|
jniLibs.srcDirs("../third-party/android/libs/executorch")
|
|
150
155
|
// Include generated codegen if using TurboModules
|
|
151
156
|
java.srcDirs("${project.buildDir}/generated/source/codegen/java")
|
|
@@ -160,6 +165,25 @@ android {
|
|
|
160
165
|
packaging {
|
|
161
166
|
// Prevents "Duplicate Library" errors with the React Native JSI engine
|
|
162
167
|
resources.excludes.add("**/libjsi.so")
|
|
168
|
+
|
|
169
|
+
// Ship only the backends the resolved config asks for. Without this an
|
|
170
|
+
// app declaring `backends: ["vulkan"]` still carries 2.67 MB of XNNPACK
|
|
171
|
+
// that nothing links, registers or loads, because jniLibs packages the
|
|
172
|
+
// directory rather than the link graph. This is the Android counterpart
|
|
173
|
+
// of the podspec deciding its -force_load entries from the same config.
|
|
174
|
+
//
|
|
175
|
+
// Keyed off the backend .so names rather than a staging dir so that a
|
|
176
|
+
// user who provisions third-party/ by hand gets the same treatment.
|
|
177
|
+
val backendLibs = mapOf(
|
|
178
|
+
"enableXnnpack" to "libxnnpack_executorch_backend.so",
|
|
179
|
+
"enableVulkan" to "libvulkan_executorch_backend.so"
|
|
180
|
+
)
|
|
181
|
+
for ((flag, soName) in backendLibs) {
|
|
182
|
+
if (rneConfig[flag] == false) {
|
|
183
|
+
logger.lifecycle("[RnExecutorch] $flag is off; excluding $soName from the APK")
|
|
184
|
+
jniLibs.excludes.add("**/$soName")
|
|
185
|
+
}
|
|
186
|
+
}
|
|
163
187
|
}
|
|
164
188
|
|
|
165
189
|
compileOptions {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
#include <algorithm>
|
|
12
12
|
#include <cstring>
|
|
13
13
|
#include <format>
|
|
14
|
+
#include <optional>
|
|
14
15
|
|
|
15
16
|
#include <executorch/extension/llm/runner/constants.h>
|
|
16
17
|
#include <executorch/extension/llm/runner/llm_runner_helper.h>
|
|
@@ -18,6 +19,8 @@
|
|
|
18
19
|
#include <executorch/extension/llm/runner/multimodal_runner.h>
|
|
19
20
|
#include <executorch/extension/llm/runner/stats.h>
|
|
20
21
|
#include <executorch/extension/llm/runner/text_llm_runner.h>
|
|
22
|
+
#include <executorch/extension/llm/runner/text_prefiller.h>
|
|
23
|
+
#include <executorch/extension/module/module.h>
|
|
21
24
|
#include <executorch/runtime/core/error.h>
|
|
22
25
|
|
|
23
26
|
#include "core/conversions.h"
|
|
@@ -56,8 +59,8 @@ namespace {
|
|
|
56
59
|
// COUPLING / MAINTENANCE WARNING:
|
|
57
60
|
// This creates a direct compile-time coupling with the internal implementation
|
|
58
61
|
// details of ExecuTorch's `TextLLMRunner` and `MultimodalRunner`. If upstream
|
|
59
|
-
// ExecuTorch renames or alters
|
|
60
|
-
//
|
|
62
|
+
// ExecuTorch renames or alters any member accessed below, this code will fail
|
|
63
|
+
// to compile and must be adjusted accordingly.
|
|
61
64
|
// ============================================================================
|
|
62
65
|
|
|
63
66
|
template <typename Tag, auto MemberPtr>
|
|
@@ -95,6 +98,96 @@ constexpr auto getPrivateMember(MMRunnerPrefillTag /*tag*/);
|
|
|
95
98
|
template struct PrivateMemberAccessor<MMRunnerMetadataTag, &MultimodalRunner::metadata_>;
|
|
96
99
|
constexpr auto getPrivateMember(MMRunnerMetadataTag /*tag*/);
|
|
97
100
|
|
|
101
|
+
// Members that decide how a prompt is split into prefill chunks.
|
|
102
|
+
using executorch::extension::llm::TextPrefiller;
|
|
103
|
+
|
|
104
|
+
struct TextRunnerModuleTag {};
|
|
105
|
+
struct TextRunnerPrefillerTag {};
|
|
106
|
+
struct PrefillerMaxSeqLenTag {};
|
|
107
|
+
struct MMRunnerModuleTag {};
|
|
108
|
+
struct MMRunnerTokenizerTag {};
|
|
109
|
+
|
|
110
|
+
template struct PrivateMemberAccessor<TextRunnerModuleTag, &TextLLMRunner::module_>;
|
|
111
|
+
constexpr auto getPrivateMember(TextRunnerModuleTag /*tag*/);
|
|
112
|
+
|
|
113
|
+
template struct PrivateMemberAccessor<TextRunnerPrefillerTag, &TextLLMRunner::text_prefiller_>;
|
|
114
|
+
constexpr auto getPrivateMember(TextRunnerPrefillerTag /*tag*/);
|
|
115
|
+
|
|
116
|
+
template struct PrivateMemberAccessor<PrefillerMaxSeqLenTag, &TextPrefiller::max_seq_len_>;
|
|
117
|
+
constexpr auto getPrivateMember(PrefillerMaxSeqLenTag /*tag*/);
|
|
118
|
+
|
|
119
|
+
template struct PrivateMemberAccessor<MMRunnerModuleTag, &MultimodalRunner::module_>;
|
|
120
|
+
constexpr auto getPrivateMember(MMRunnerModuleTag /*tag*/);
|
|
121
|
+
|
|
122
|
+
template struct PrivateMemberAccessor<MMRunnerTokenizerTag, &MultimodalRunner::tokenizer_>;
|
|
123
|
+
constexpr auto getPrivateMember(MMRunnerTokenizerTag /*tag*/);
|
|
124
|
+
|
|
125
|
+
// Upstream sizes prefill chunks from `get_max_seq_len`, which is the KV budget,
|
|
126
|
+
// not the widest token input the graph accepts (e.g. gemma4 MLX: 2048 vs 511).
|
|
127
|
+
// Prompts past the real bound then fail with `Error::NotSupported`. Returns the
|
|
128
|
+
// bound declared by the method's first input, or nullopt if it is not a
|
|
129
|
+
// rank>=2 integer tensor.
|
|
130
|
+
std::optional<int64_t> readPrefillBound(executorch::extension::Module *module, const std::string &methodName) {
|
|
131
|
+
if (module == nullptr) {
|
|
132
|
+
return std::nullopt;
|
|
133
|
+
}
|
|
134
|
+
auto meta = module->method_meta(methodName);
|
|
135
|
+
if (!meta.ok() || meta->num_inputs() == 0) {
|
|
136
|
+
return std::nullopt;
|
|
137
|
+
}
|
|
138
|
+
auto inputMeta = meta->input_tensor_meta(0);
|
|
139
|
+
if (!inputMeta.ok()) {
|
|
140
|
+
return std::nullopt;
|
|
141
|
+
}
|
|
142
|
+
const auto dtype = inputMeta->scalar_type();
|
|
143
|
+
if (dtype != executorch::aten::ScalarType::Long && dtype != executorch::aten::ScalarType::Int) {
|
|
144
|
+
return std::nullopt;
|
|
145
|
+
}
|
|
146
|
+
const auto sizes = inputMeta->sizes();
|
|
147
|
+
if (sizes.size() < 2) {
|
|
148
|
+
return std::nullopt;
|
|
149
|
+
}
|
|
150
|
+
const auto bound = static_cast<int64_t>(sizes[sizes.size() - 1]);
|
|
151
|
+
if (bound <= 0) {
|
|
152
|
+
return std::nullopt;
|
|
153
|
+
}
|
|
154
|
+
return bound;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
::tokenizers::Tokenizer *getRunnerTokenizer(executorch::extension::llm::IRunner *runner) {
|
|
158
|
+
auto *r = dynamic_cast<MultimodalRunner *>(runner);
|
|
159
|
+
if (r == nullptr) {
|
|
160
|
+
return nullptr;
|
|
161
|
+
}
|
|
162
|
+
return (r->*getPrivateMember(MMRunnerTokenizerTag{})).get();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// `MultimodalPrefiller` does not chunk, so split text into token inputs that fit
|
|
166
|
+
// the bound; `MultimodalRunner` prefills them in order.
|
|
167
|
+
void appendTextInput(std::vector<executorch::extension::llm::MultimodalInput> &inputs,
|
|
168
|
+
std::string text,
|
|
169
|
+
::tokenizers::Tokenizer *tokenizer,
|
|
170
|
+
int64_t prefillBound) {
|
|
171
|
+
if (tokenizer == nullptr || prefillBound <= 0) {
|
|
172
|
+
inputs.emplace_back(std::move(text));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
auto encoded = tokenizer->encode(text, 0, 0);
|
|
177
|
+
if (!encoded.ok()) {
|
|
178
|
+
inputs.emplace_back(std::move(text));
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const auto &tokens = encoded.get();
|
|
183
|
+
const auto chunk = static_cast<size_t>(prefillBound);
|
|
184
|
+
for (size_t offset = 0; offset < tokens.size(); offset += chunk) {
|
|
185
|
+
const auto end = std::min(offset + chunk, tokens.size());
|
|
186
|
+
inputs.emplace_back(std::vector<uint64_t>(tokens.begin() + static_cast<ptrdiff_t>(offset),
|
|
187
|
+
tokens.begin() + static_cast<ptrdiff_t>(end)));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
98
191
|
int64_t getRunnerPos(executorch::extension::llm::IRunner *runner, bool isMultimodal) {
|
|
99
192
|
if (runner == nullptr) {
|
|
100
193
|
return 0;
|
|
@@ -178,10 +271,14 @@ std::vector<executorch::extension::llm::MultimodalInput> parsePrompt(
|
|
|
178
271
|
jsi::Runtime &rt,
|
|
179
272
|
const std::string &ctx,
|
|
180
273
|
const jsi::Value &value,
|
|
181
|
-
const std::vector<std::string> &supportedModalities
|
|
274
|
+
const std::vector<std::string> &supportedModalities,
|
|
275
|
+
::tokenizers::Tokenizer *tokenizer,
|
|
276
|
+
int64_t prefillBound) {
|
|
182
277
|
|
|
183
278
|
if (value.isString()) {
|
|
184
|
-
|
|
279
|
+
std::vector<executorch::extension::llm::MultimodalInput> inputs;
|
|
280
|
+
appendTextInput(inputs, conversions::asType<std::string>(rt, ctx, value), tokenizer, prefillBound);
|
|
281
|
+
return inputs;
|
|
185
282
|
}
|
|
186
283
|
|
|
187
284
|
auto arr = conversions::asType<jsi::Array>(rt, ctx, value);
|
|
@@ -195,7 +292,7 @@ std::vector<executorch::extension::llm::MultimodalInput> parsePrompt(
|
|
|
195
292
|
std::string itemCtx = std::format("{}[{}]", ctx, i);
|
|
196
293
|
|
|
197
294
|
if (elem.isString()) {
|
|
198
|
-
inputs
|
|
295
|
+
appendTextInput(inputs, conversions::asType<std::string>(rt, itemCtx, elem), tokenizer, prefillBound);
|
|
199
296
|
continue;
|
|
200
297
|
}
|
|
201
298
|
auto mediaObj = conversions::asType<jsi::Object>(rt, itemCtx, elem);
|
|
@@ -254,10 +351,16 @@ LLMRunnerHostObject::LLMRunnerHostObject(const std::string &modelPath,
|
|
|
254
351
|
throw error::LoadFailed(std::format("LLMRunner: Failed to load runner tokenizer at path: {}", tokenizerPath));
|
|
255
352
|
}
|
|
256
353
|
|
|
354
|
+
// Plain mmap leaves the weights as clean, file-backed pages the kernel
|
|
355
|
+
// can evict, which is what the legacy binding has always passed.
|
|
356
|
+
constexpr auto kLoadMode = executorch::extension::Module::LoadMode::Mmap;
|
|
357
|
+
|
|
257
358
|
if (modalities_.empty()) {
|
|
258
|
-
runner_ = executorch::extension::llm::create_text_llm_runner(
|
|
359
|
+
runner_ = executorch::extension::llm::create_text_llm_runner(
|
|
360
|
+
modelPath, std::move(tokenizer), std::nullopt, -1.0f, "forward", kLoadMode);
|
|
259
361
|
} else {
|
|
260
|
-
runner_ = executorch::extension::llm::create_multimodal_runner(
|
|
362
|
+
runner_ = executorch::extension::llm::create_multimodal_runner(
|
|
363
|
+
modelPath, std::move(tokenizer), std::nullopt, kLoadMode);
|
|
261
364
|
}
|
|
262
365
|
|
|
263
366
|
if (!runner_) {
|
|
@@ -269,6 +372,33 @@ LLMRunnerHostObject::LLMRunnerHostObject(const std::string &modelPath,
|
|
|
269
372
|
std::string errorMsg = executorch::runtime::to_string(loadStatus);
|
|
270
373
|
throw error::LoadFailed(std::format("LLMRunner: Failed to load model: {}", errorMsg), loadStatus);
|
|
271
374
|
}
|
|
375
|
+
|
|
376
|
+
// Clamp prefill chunks to the graph's real bound (see `readPrefillBound`).
|
|
377
|
+
// Text models take tokens on `forward`, multimodal ones on `token_embedding`.
|
|
378
|
+
if (modalities_.empty()) {
|
|
379
|
+
auto *textRunner = dynamic_cast<TextLLMRunner *>(runner_.get());
|
|
380
|
+
if (textRunner != nullptr) {
|
|
381
|
+
const auto &module = textRunner->*getPrivateMember(TextRunnerModuleTag{});
|
|
382
|
+
const auto bound = readPrefillBound(module.get(), "forward");
|
|
383
|
+
auto &prefiller = textRunner->*getPrivateMember(TextRunnerPrefillerTag{});
|
|
384
|
+
if (bound.has_value() && prefiller) {
|
|
385
|
+
// Upstream allows max_seq_len < max_context_len and chunks itself.
|
|
386
|
+
auto &maxSeqLen = (*prefiller).*getPrivateMember(PrefillerMaxSeqLenTag{});
|
|
387
|
+
maxSeqLen = std::min(maxSeqLen, *bound);
|
|
388
|
+
prefillBound_ = maxSeqLen;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
} else {
|
|
392
|
+
auto *mmRunner = dynamic_cast<MultimodalRunner *>(runner_.get());
|
|
393
|
+
if (mmRunner != nullptr) {
|
|
394
|
+
const auto &module = mmRunner->*getPrivateMember(MMRunnerModuleTag{});
|
|
395
|
+
const auto bound =
|
|
396
|
+
readPrefillBound(module.get(), executorch::extension::llm::kTokenEmbeddingMethod);
|
|
397
|
+
if (bound.has_value()) {
|
|
398
|
+
prefillBound_ = *bound;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
272
402
|
}
|
|
273
403
|
|
|
274
404
|
std::unique_lock<std::mutex> LLMRunnerHostObject::tryLockUnique(std::string_view ctx) {
|
|
@@ -305,11 +435,9 @@ jsi::Value LLMRunnerHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &nam
|
|
|
305
435
|
}
|
|
306
436
|
|
|
307
437
|
executorch::extension::llm::GenerationConfig config;
|
|
438
|
+
config.echo = false;
|
|
308
439
|
if (count > 1 && !args[1].isUndefined() && !args[1].isNull()) {
|
|
309
440
|
auto configObj = conversions::asType<jsi::Object>(rt, "LLMRunner.generate: config", args[1]);
|
|
310
|
-
if (auto echoOpt = conversions::getOptionalProperty<bool>(rt, "LLMRunner.generate: config", configObj, "echo")) {
|
|
311
|
-
config.echo = *echoOpt;
|
|
312
|
-
}
|
|
313
441
|
if (auto ignoreEosOpt = conversions::getOptionalProperty<bool>(rt, "LLMRunner.generate: config", configObj, "ignoreEos")) {
|
|
314
442
|
config.ignore_eos = *ignoreEosOpt;
|
|
315
443
|
}
|
|
@@ -348,7 +476,8 @@ jsi::Value LLMRunnerHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &nam
|
|
|
348
476
|
auto prompt = conversions::asType<std::string>(rt, "LLMRunner.generate: prompt", args[0]);
|
|
349
477
|
genStatus = self->runner_->generate(prompt, config, tokenCallback, statsCallback);
|
|
350
478
|
} else {
|
|
351
|
-
auto inputs = parsePrompt(rt, "LLMRunner.generate", args[0], self->modalities_
|
|
479
|
+
auto inputs = parsePrompt(rt, "LLMRunner.generate", args[0], self->modalities_,
|
|
480
|
+
getRunnerTokenizer(self->runner_.get()), self->prefillBound_);
|
|
352
481
|
auto *multimodalRunner = dynamic_cast<executorch::extension::llm::MultimodalRunner *>(self->runner_.get());
|
|
353
482
|
if (multimodalRunner == nullptr) {
|
|
354
483
|
throw error::InvalidArgument("LLMRunner.generate: Runner instance is not a multimodal model");
|
|
@@ -375,7 +504,8 @@ jsi::Value LLMRunnerHostObject::get(jsi::Runtime &rt, const jsi::PropNameID &nam
|
|
|
375
504
|
|
|
376
505
|
auto lock = self->tryLockUnique("LLMRunner.prefill");
|
|
377
506
|
|
|
378
|
-
auto inputs = parsePrompt(rt, "LLMRunner.prefill", args[0], self->modalities_
|
|
507
|
+
auto inputs = parsePrompt(rt, "LLMRunner.prefill", args[0], self->modalities_,
|
|
508
|
+
getRunnerTokenizer(self->runner_.get()), self->prefillBound_);
|
|
379
509
|
auto result = self->runner_->prefill(inputs);
|
|
380
510
|
|
|
381
511
|
if (!result.ok()) {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
9
9
|
#endif
|
|
10
10
|
|
|
11
|
+
#include <cstdint>
|
|
11
12
|
#include <memory>
|
|
12
13
|
#include <mutex>
|
|
13
14
|
#include <string>
|
|
@@ -77,6 +78,12 @@ private:
|
|
|
77
78
|
std::string tokenizerPath_;
|
|
78
79
|
/** List of supported non-text input modalities. */
|
|
79
80
|
std::vector<std::string> modalities_;
|
|
81
|
+
/**
|
|
82
|
+
* Longest token sequence the loaded graph accepts in one prefill step, read
|
|
83
|
+
* off the model's own method metadata. 0 when the model does not declare
|
|
84
|
+
* one, in which case prompts are passed through unsplit.
|
|
85
|
+
*/
|
|
86
|
+
int64_t prefillBound_ = 0;
|
|
80
87
|
};
|
|
81
88
|
|
|
82
89
|
void install_createLLMRunner(facebook::jsi::Runtime &rt, facebook::jsi::Object &module);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["rnexecutorchJsi","createLLMRunner","modelPath","tokenizerPath","modalities","llm"],"sourceRoot":"../../../../src","sources":["extensions/llm/llmRunner.ts"],"mappings":";;AAAA;AACA;AACA;;AAGA,SAASA,eAAe,QAAQ,wBAAqB;;AAIrD;AACA;AACA;AACA;AACA;;
|
|
1
|
+
{"version":3,"names":["rnexecutorchJsi","createLLMRunner","modelPath","tokenizerPath","modalities","llm"],"sourceRoot":"../../../../src","sources":["extensions/llm/llmRunner.ts"],"mappings":";;AAAA;AACA;AACA;;AAGA,SAASA,eAAe,QAAQ,wBAAqB;;AAIrD;AACA;AACA;AACA;AACA;;AAUA;AACA;AACA;AACA;AACA;;AAoBA;AACA;AACA;AACA;AACA;;AAKA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;;AAGA;AACA;AACA;AACA;AACA;;AAYA;AACA;AACA;AACA;AACA;AACA;AACA;;AAyDA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,eAAeA,CAC7BC,SAAiB,EACjBC,aAAqB,EACrBC,UAAgC,EACrB;EACX,SAAS;;EACT,OAAOJ,eAAe,CAACK,GAAG,CAACJ,eAAe,CAACC,SAAS,EAAEC,aAAa,EAAEC,UAAU,IAAI,EAAE,CAAC;AACxF","ignoreList":[]}
|
|
@@ -43,13 +43,13 @@ function generateChatTurnWorklet(runner, prompt, options) {
|
|
|
43
43
|
|
|
44
44
|
const {
|
|
45
45
|
genConfig,
|
|
46
|
-
|
|
46
|
+
stopTokens,
|
|
47
47
|
stopRegex,
|
|
48
48
|
onToken
|
|
49
49
|
} = options;
|
|
50
50
|
let response = '';
|
|
51
51
|
const callback = token => {
|
|
52
|
-
if (token
|
|
52
|
+
if (stopTokens.includes(token)) return;
|
|
53
53
|
response += token;
|
|
54
54
|
if (onToken) scheduleOnRN(onToken, token);
|
|
55
55
|
if (stopRegex?.test(response)) runner.stop();
|
|
@@ -99,7 +99,7 @@ export async function createLLMChatSession(config, options = {}, runtime) {
|
|
|
99
99
|
const tokenizerConfig = parseTokenizerConfig(JSON.parse(tokenizerConfigStr));
|
|
100
100
|
const {
|
|
101
101
|
chatTemplate,
|
|
102
|
-
|
|
102
|
+
stopTokens
|
|
103
103
|
} = tokenizerConfig;
|
|
104
104
|
|
|
105
105
|
// Prepare chat preprocessor
|
|
@@ -139,7 +139,7 @@ export async function createLLMChatSession(config, options = {}, runtime) {
|
|
|
139
139
|
};
|
|
140
140
|
const generationOpts = {
|
|
141
141
|
genConfig: turnGenConfig,
|
|
142
|
-
|
|
142
|
+
stopTokens,
|
|
143
143
|
stopRegex,
|
|
144
144
|
onToken
|
|
145
145
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["scheduleOnRN","RNBlobUtil","wrapAsync","createResourceScope","createLLMRunner","parseTokenizerConfig","createChatPreprocessor","generateChatTurnWorklet","runner","prompt","options","genConfig","
|
|
1
|
+
{"version":3,"names":["scheduleOnRN","RNBlobUtil","wrapAsync","createResourceScope","createLLMRunner","parseTokenizerConfig","createChatPreprocessor","generateChatTurnWorklet","runner","prompt","options","genConfig","stopTokens","stopRegex","onToken","response","callback","token","includes","test","stop","stats","generate","DEFAULT_MAX_TURNS","createLLMChatSession","config","runtime","scope","dispose","generationConfig","defaultGenerationConfig","initialMessages","toolOpts","resetOnTurn","modelPath","tokenizerPath","tokenizerConfigPath","modalities","preprocessorConfig","tools","parseToolCalls","maxToolTurns","tokenizerConfigStr","fs","readFile","tokenizerConfig","JSON","parse","chatTemplate","chatPreprocessorConfig","chatPreprocessor","track","prefill","history","committed","length","push","process","addGenPrompt","clear","generateChatTurn","sendMessage","message","turnGenConfig","generationOpts","initialCommitted","initialPos","getKVCacheState","pos","turnStartIdx","generationStatsList","role","content","reset","prefillStartMs","Date","now","toCommit","userPrompt","posAtEndOfUser","finishReason","currentTurn","uncommitted","prefillDurationMs","parsedTools","toolCalls","textContent","toolCall","tool","find","t","function","name","toolContent","execute","arguments","err","String","toolCallId","id","messages","slice","getHistory","error"],"sourceRoot":"../../../../../src","sources":["extensions/llm/tasks/llmChatSession.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -43,9 +43,11 @@ export function parseTokenizerConfig(config) {
|
|
|
43
43
|
if (!eosToken) {
|
|
44
44
|
throw RnExecuTorchError('LOAD_FAILED', 'tokenizer_config.json does not define required `eos_token` string');
|
|
45
45
|
}
|
|
46
|
+
const stopTokens = [eosToken, resolveToken(config.eot_token), resolveToken(config.pad_token)].filter((token, index, all) => !!token && all.indexOf(token) === index);
|
|
46
47
|
return {
|
|
47
48
|
chatTemplate,
|
|
48
|
-
eosToken
|
|
49
|
+
eosToken,
|
|
50
|
+
stopTokens
|
|
49
51
|
};
|
|
50
52
|
}
|
|
51
53
|
//# sourceMappingURL=tokenizerConfig.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["RnExecuTorchError","resolveToken","token","content","undefined","parseTokenizerConfig","config","chatTemplate","chat_template","Array","isArray","entry","find","t","name","template","eosToken","eos_token"],"sourceRoot":"../../../../../src","sources":["extensions/llm/utils/tokenizerConfig.ts"],"mappings":";;AAAA;AACA;AACA;AACA;;AAEA,SAASA,iBAAiB,QAAQ,wBAAqB;;AAEvD;AACA;AACA;AACA;;
|
|
1
|
+
{"version":3,"names":["RnExecuTorchError","resolveToken","token","content","undefined","parseTokenizerConfig","config","chatTemplate","chat_template","Array","isArray","entry","find","t","name","template","eosToken","eos_token","stopTokens","eot_token","pad_token","filter","index","all","indexOf"],"sourceRoot":"../../../../../src","sources":["extensions/llm/utils/tokenizerConfig.ts"],"mappings":";;AAAA;AACA;AACA;AACA;;AAEA,SAASA,iBAAiB,QAAQ,wBAAqB;;AAEvD;AACA;AACA;AACA;;AAYA,SAASC,YAAYA,CAACC,KAAc,EAAsB;EACxD,IAAI,OAAOA,KAAK,KAAK,QAAQ,EAAE,OAAOA,KAAK;EAC3C,IAAIA,KAAK,IAAI,OAAOA,KAAK,KAAK,QAAQ,IAAI,OAAQA,KAAK,CAASC,OAAO,KAAK,QAAQ,EAAE;IACpF,OAAQD,KAAK,CAASC,OAAO;EAC/B;EACA,OAAOC,SAAS;AAClB;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,oBAAoBA,CAACC,MAAW,EAAuB;EACrE,IAAIC,YAAY,GAAGD,MAAM,CAACE,aAAa;;EAEvC;EACA,IAAIC,KAAK,CAACC,OAAO,CAACH,YAAY,CAAC,EAAE;IAC/B,MAAMI,KAAK,GAAGJ,YAAY,CAACK,IAAI,CAAEC,CAAC,IAAKA,CAAC,EAAEC,IAAI,KAAK,SAAS,CAAC,IAAIP,YAAY,CAAC,CAAC,CAAC;IAChFA,YAAY,GAAGI,KAAK,EAAEI,QAAQ;EAChC;EAEA,IAAI,OAAOR,YAAY,KAAK,QAAQ,EAAE;IACpC,MAAMP,iBAAiB,CACrB,aAAa,EACb,iEACF,CAAC;EACH;EAEA,MAAMgB,QAAQ,GAAGf,YAAY,CAACK,MAAM,CAACW,SAAS,CAAC;EAE/C,IAAI,CAACD,QAAQ,EAAE;IACb,MAAMhB,iBAAiB,CACrB,aAAa,EACb,mEACF,CAAC;EACH;EAEA,MAAMkB,UAAU,GAAG,CACjBF,QAAQ,EACRf,YAAY,CAACK,MAAM,CAACa,SAAS,CAAC,EAC9BlB,YAAY,CAACK,MAAM,CAACc,SAAS,CAAC,CAC/B,CAACC,MAAM,CAAC,CAACnB,KAAK,EAAEoB,KAAK,EAAEC,GAAG,KAAsB,CAAC,CAACrB,KAAK,IAAIqB,GAAG,CAACC,OAAO,CAACtB,KAAK,CAAC,KAAKoB,KAAK,CAAC;EAEzF,OAAO;IAAEf,YAAY;IAAES,QAAQ;IAAEE;EAAW,CAAC;AAC/C","ignoreList":[]}
|
|
@@ -12,7 +12,7 @@ const DOWNLOAD_EVENT_ENDPOINT = 'https://ai.swmansion.com/telemetry/downloads/ap
|
|
|
12
12
|
// self-referencing import would need package `exports` support in the consuming
|
|
13
13
|
// bundler to load at all. A wrong value here is analytics noise; a failed
|
|
14
14
|
// import would break the bundle.
|
|
15
|
-
const LIB_VERSION = '0.10.
|
|
15
|
+
const LIB_VERSION = '0.10.3';
|
|
16
16
|
|
|
17
17
|
// Anonymous analytics are on by default; apps opt out via setTelemetryEnabled.
|
|
18
18
|
let telemetryEnabled = true;
|
|
@@ -8,8 +8,6 @@ import type { Tensor } from '../../core/tensor';
|
|
|
8
8
|
* @category LLM / Types
|
|
9
9
|
*/
|
|
10
10
|
export type LLMGenerationConfig = {
|
|
11
|
-
/** Whether to echo the prompt in the generated output. */
|
|
12
|
-
readonly echo?: boolean;
|
|
13
11
|
/** Whether to ignore EOS tokens during generation. */
|
|
14
12
|
readonly ignoreEos?: boolean;
|
|
15
13
|
/** Maximum number of new tokens to generate. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llmRunner.d.ts","sourceRoot":"","sources":["../../../../../src/extensions/llm/llmRunner.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAKhD;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,
|
|
1
|
+
{"version":3,"file":"llmRunner.d.ts","sourceRoot":"","sources":["../../../../../src/extensions/llm/llmRunner.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,10 @@ export type TokenizerChatConfig = {
|
|
|
11
11
|
readonly chatTemplate: string;
|
|
12
12
|
/** End-of-sequence token string. */
|
|
13
13
|
readonly eosToken: string;
|
|
14
|
+
/**
|
|
15
|
+
* Every terminal token the model may decode into its output.
|
|
16
|
+
*/
|
|
17
|
+
readonly stopTokens: readonly string[];
|
|
14
18
|
};
|
|
15
19
|
/**
|
|
16
20
|
* Parses raw JSON configuration from `tokenizer_config.json` into a normalized format.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenizerConfig.d.ts","sourceRoot":"","sources":["../../../../../../src/extensions/llm/utils/tokenizerConfig.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,uDAAuD;IACvD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,oCAAoC;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"tokenizerConfig.d.ts","sourceRoot":"","sources":["../../../../../../src/extensions/llm/utils/tokenizerConfig.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,uDAAuD;IACvD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,oCAAoC;IACpC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC,CAAC;AAUF;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,GAAG,GAAG,mBAAmB,CAgCrE"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-executorch",
|
|
3
|
-
"version": "0.10.
|
|
4
|
-
"nativeLibsVersion": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
|
+
"nativeLibsVersion": "0.10.4",
|
|
5
5
|
"description": "An easy way to run AI models in React Native with ExecuTorch",
|
|
6
6
|
"main": "./lib/module/index.js",
|
|
7
7
|
"module": "./lib/module/index.js",
|
package/scripts/download-libs.js
CHANGED
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
*
|
|
12
12
|
* headers.tar.gz -- ExecuTorch + c10 + torch + tokenizers + opencv
|
|
13
13
|
* headers (platform-independent; always downloaded)
|
|
14
|
-
* core-android-arm64-v8a.tar.gz --
|
|
15
|
-
*
|
|
16
|
-
* core-
|
|
14
|
+
* core-android-arm64-v8a.tar.gz -- libexecutorch.so (+ executorch.jar) for arm64;
|
|
15
|
+
* pthreadpool and cpuinfo are statically linked in
|
|
16
|
+
* core-android-x86_64.tar.gz -- libexecutorch.so for x86_64
|
|
17
|
+
* core-ios.tar.gz -- ExecutorchLib.xcframework + libthreadpool_*.a
|
|
17
18
|
* opencv-android-arm64-v8a.tar.gz -- OpenCV for arm64
|
|
18
19
|
* opencv-android-x86_64.tar.gz -- OpenCV for x86_64
|
|
19
20
|
* opencv-ios.tar.gz -- OpenCV xcframework
|
|
@@ -74,7 +75,9 @@ const https = require('https');
|
|
|
74
75
|
const http = require('http');
|
|
75
76
|
const fs = require('fs');
|
|
76
77
|
const path = require('path');
|
|
77
|
-
const {
|
|
78
|
+
const { spawnSync } = require('child_process');
|
|
79
|
+
const { createHash } = require('crypto');
|
|
80
|
+
const { Buffer } = require('buffer');
|
|
78
81
|
|
|
79
82
|
// ---- Config ----------------------------------------------------------------
|
|
80
83
|
|
|
@@ -151,8 +154,10 @@ const FEATURE_MAP = {
|
|
|
151
154
|
// YOLO is xnnpack-only, ssdlite/rf_detr add coreml → union.
|
|
152
155
|
objectDetection: { backends: ['xnnpack', 'coreml'], libs: ['opencv'] },
|
|
153
156
|
// Keypoint detection (#1280): BlazeFace + YOLO26-pose ship xnnpack; RF-DETR
|
|
154
|
-
// keypoint adds coreml
|
|
155
|
-
//
|
|
157
|
+
// keypoint adds coreml → union. mlx stays for the legacy API, whose
|
|
158
|
+
// RF_DETR_KEYPOINT_PREVIEW_MLX_FP32_MODEL outlived the new registry's MLX
|
|
159
|
+
// keypoint export (dropped in #1418).
|
|
160
|
+
// (Named to track the useKeypointDetector hook; main calls this poseEstimation.)
|
|
156
161
|
keypointDetection: { backends: ['xnnpack', 'coreml', 'mlx'], libs: ['opencv'] },
|
|
157
162
|
// DeepLab/FCN/LR-ASPP/selfie all ship xnnpack + coreml.
|
|
158
163
|
semanticSegmentation: { backends: ['xnnpack', 'coreml'], libs: ['opencv'] },
|
|
@@ -431,15 +436,33 @@ function download(url, dest) {
|
|
|
431
436
|
};
|
|
432
437
|
get(url);
|
|
433
438
|
file.on('error', (err) => {
|
|
434
|
-
|
|
439
|
+
// Close before removing: on Windows an open handle makes the unlink fail
|
|
440
|
+
// with EBUSY, which then replaces the real error with the cleanup's. And
|
|
441
|
+
// `force` covers the case where the file was never created.
|
|
442
|
+
file.close(() => fs.rmSync(dest, { force: true }));
|
|
435
443
|
reject(err);
|
|
436
444
|
});
|
|
437
445
|
});
|
|
438
446
|
}
|
|
439
447
|
|
|
448
|
+
// Hashes in-process rather than shelling out to `sha256sum` / `shasum`. Those
|
|
449
|
+
// are not on PATH everywhere, and GNU `sha256sum` escapes a filename holding a
|
|
450
|
+
// backslash by prefixing its whole output line with one -- so on Windows every
|
|
451
|
+
// hash came back as `\<hex>` and no artifact could ever validate. Read in
|
|
452
|
+
// chunks: some artifacts are hundreds of megabytes and this runs at install.
|
|
440
453
|
function sha256(filePath) {
|
|
441
|
-
const
|
|
442
|
-
|
|
454
|
+
const hash = createHash('sha256');
|
|
455
|
+
const buffer = Buffer.alloc(1024 * 1024);
|
|
456
|
+
const fd = fs.openSync(filePath, 'r');
|
|
457
|
+
try {
|
|
458
|
+
let bytesRead;
|
|
459
|
+
while ((bytesRead = fs.readSync(fd, buffer, 0, buffer.length, null)) > 0) {
|
|
460
|
+
hash.update(buffer.subarray(0, bytesRead));
|
|
461
|
+
}
|
|
462
|
+
} finally {
|
|
463
|
+
fs.closeSync(fd);
|
|
464
|
+
}
|
|
465
|
+
return hash.digest('hex');
|
|
443
466
|
}
|
|
444
467
|
|
|
445
468
|
async function isCacheValid(artifact) {
|
|
@@ -461,6 +484,68 @@ async function isCacheValid(artifact) {
|
|
|
461
484
|
return expectedChecksum === actualChecksum;
|
|
462
485
|
}
|
|
463
486
|
|
|
487
|
+
// The backend binaries each artifact owns, relative to the extraction dir. Used
|
|
488
|
+
// to clear stale copies: nothing else removes them, and `isCacheValid()`
|
|
489
|
+
// short-circuits the download, so turning a backend OFF after an install would
|
|
490
|
+
// otherwise leave the old binary in place and (on Android) keep shipping it.
|
|
491
|
+
const BACKEND_FILES = {
|
|
492
|
+
android: {
|
|
493
|
+
xnnpack: ['executorch/*/libxnnpack_executorch_backend.so'],
|
|
494
|
+
vulkan: ['executorch/*/libvulkan_executorch_backend.so'],
|
|
495
|
+
},
|
|
496
|
+
ios: {
|
|
497
|
+
xnnpack: ['XnnpackBackend.xcframework'],
|
|
498
|
+
coreml: ['CoreMLBackend.xcframework'],
|
|
499
|
+
mlx: ['MLXBackend.xcframework', 'libs/executorch/mlx.metallib'],
|
|
500
|
+
},
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
// Removes the binaries of backends the resolved config does not ask for, so a
|
|
504
|
+
// config change takes effect without anyone deleting third-party/ by hand.
|
|
505
|
+
function pruneDisabledBackends(targets, { backends }) {
|
|
506
|
+
for (const target of targets) {
|
|
507
|
+
const platform = target.startsWith('android') ? 'android' : 'ios';
|
|
508
|
+
const destDir =
|
|
509
|
+
platform === 'android'
|
|
510
|
+
? path.join(THIRD_PARTY_DIR, 'android', 'libs')
|
|
511
|
+
: path.join(THIRD_PARTY_DIR, 'ios');
|
|
512
|
+
if (!fs.existsSync(destDir)) continue;
|
|
513
|
+
|
|
514
|
+
for (const [backend, patterns] of Object.entries(BACKEND_FILES[platform])) {
|
|
515
|
+
if (backends.includes(backend)) continue;
|
|
516
|
+
for (const pattern of patterns) {
|
|
517
|
+
for (const stale of expandPattern(destDir, pattern)) {
|
|
518
|
+
console.log(
|
|
519
|
+
`[react-native-executorch] Removing ${backend} binary (not enabled): ${path.relative(THIRD_PARTY_DIR, stale)}`
|
|
520
|
+
);
|
|
521
|
+
fs.rmSync(stale, { recursive: true, force: true });
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Resolves a path that may contain a single `*` segment (the Android ABI dir).
|
|
529
|
+
// A dependency-free stand-in for a glob, since this script runs at postinstall
|
|
530
|
+
// with nothing but Node's stdlib available.
|
|
531
|
+
function expandPattern(root, pattern) {
|
|
532
|
+
const segments = pattern.split('/');
|
|
533
|
+
let candidates = [root];
|
|
534
|
+
for (const segment of segments) {
|
|
535
|
+
const next = [];
|
|
536
|
+
for (const base of candidates) {
|
|
537
|
+
if (segment === '*') {
|
|
538
|
+
if (!fs.existsSync(base)) continue;
|
|
539
|
+
for (const entry of fs.readdirSync(base)) next.push(path.join(base, entry));
|
|
540
|
+
} else {
|
|
541
|
+
next.push(path.join(base, segment));
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
candidates = next;
|
|
545
|
+
}
|
|
546
|
+
return candidates.filter((candidate) => fs.existsSync(candidate));
|
|
547
|
+
}
|
|
548
|
+
|
|
464
549
|
function extract(tarball, destDir) {
|
|
465
550
|
ensureDir(destDir);
|
|
466
551
|
// `-m` stamps extracted files with the extraction time instead of the mtime
|
|
@@ -471,7 +556,36 @@ function extract(tarball, destDir) {
|
|
|
471
556
|
// and they get archived and linked against the new libraries -- which shows
|
|
472
557
|
// up as an undefined symbol for whatever API changed between the two
|
|
473
558
|
// releases, far away from the actual cause.
|
|
474
|
-
|
|
559
|
+
//
|
|
560
|
+
// No path reaches tar's argv, because GNU tar (Git for Windows' is the one
|
|
561
|
+
// on PATH under Git Bash) mangles Windows paths two ways: `-f C:\Users\...`
|
|
562
|
+
// is the `host:file` form, so it tries to reach a remote host named `C`; and
|
|
563
|
+
// `-C` is unquoted by default, so the `\r` and `\t` in
|
|
564
|
+
// `node_modules\react-native-executorch\third-party` become a carriage
|
|
565
|
+
// return and a tab. The archive arrives on stdin and `cwd` does the chdir.
|
|
566
|
+
const fd = fs.openSync(tarball, 'r');
|
|
567
|
+
let result;
|
|
568
|
+
try {
|
|
569
|
+
result = spawnSync('tar', ['-xzm', '-f', '-'], {
|
|
570
|
+
cwd: destDir,
|
|
571
|
+
stdio: [fd, 'inherit', 'pipe'],
|
|
572
|
+
encoding: 'utf8',
|
|
573
|
+
});
|
|
574
|
+
} finally {
|
|
575
|
+
fs.closeSync(fd);
|
|
576
|
+
}
|
|
577
|
+
if (result.error) {
|
|
578
|
+
throw new Error(
|
|
579
|
+
`Could not run \`tar\` to extract ${tarball}: ${result.error.message}. ` +
|
|
580
|
+
'Install tar (it ships with Windows 10 1803+, Git for Windows and every Unix) and reinstall.'
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
if (result.status !== 0) {
|
|
584
|
+
throw new Error(
|
|
585
|
+
`tar exited with ${result.status ?? result.signal} while extracting ${tarball}` +
|
|
586
|
+
`${result.stderr ? `:\n${result.stderr.trim()}` : ''}`
|
|
587
|
+
);
|
|
588
|
+
}
|
|
475
589
|
}
|
|
476
590
|
|
|
477
591
|
// ---- Main ------------------------------------------------------------------
|
|
@@ -525,6 +639,10 @@ async function main() {
|
|
|
525
639
|
console.log(` ✓ Done`);
|
|
526
640
|
}
|
|
527
641
|
|
|
642
|
+
// Belt and braces to core no longer carrying backends: this also clears
|
|
643
|
+
// binaries left by an EARLIER install that had the backend enabled.
|
|
644
|
+
pruneDisabledBackends(targets, config);
|
|
645
|
+
|
|
528
646
|
console.log('[react-native-executorch] Native libs ready.');
|
|
529
647
|
}
|
|
530
648
|
|
|
@@ -536,4 +654,14 @@ if (require.main === module) {
|
|
|
536
654
|
});
|
|
537
655
|
}
|
|
538
656
|
|
|
539
|
-
module.exports = {
|
|
657
|
+
module.exports = {
|
|
658
|
+
ALL_BACKENDS,
|
|
659
|
+
ALL_LIBS,
|
|
660
|
+
BACKEND_FILES,
|
|
661
|
+
FEATURE_MAP,
|
|
662
|
+
findUserConfig,
|
|
663
|
+
readUserConfig,
|
|
664
|
+
pruneDisabledBackends,
|
|
665
|
+
sha256,
|
|
666
|
+
extract,
|
|
667
|
+
};
|
|
@@ -13,8 +13,6 @@ declare const llmRunnerBrand: unique symbol;
|
|
|
13
13
|
* @category LLM / Types
|
|
14
14
|
*/
|
|
15
15
|
export type LLMGenerationConfig = {
|
|
16
|
-
/** Whether to echo the prompt in the generated output. */
|
|
17
|
-
readonly echo?: boolean;
|
|
18
16
|
/** Whether to ignore EOS tokens during generation. */
|
|
19
17
|
readonly ignoreEos?: boolean;
|
|
20
18
|
/** Maximum number of new tokens to generate. */
|
|
@@ -139,18 +139,18 @@ function generateChatTurnWorklet(
|
|
|
139
139
|
prompt: Prompt,
|
|
140
140
|
options: {
|
|
141
141
|
readonly genConfig: LLMGenerationConfig;
|
|
142
|
-
readonly
|
|
142
|
+
readonly stopTokens: readonly string[];
|
|
143
143
|
readonly stopRegex?: RegExp;
|
|
144
144
|
readonly onToken?: (token: string) => void;
|
|
145
145
|
}
|
|
146
146
|
): { readonly response: string; readonly stats: LLMGenerationStats } {
|
|
147
147
|
'worklet';
|
|
148
|
-
const { genConfig,
|
|
148
|
+
const { genConfig, stopTokens, stopRegex, onToken } = options;
|
|
149
149
|
|
|
150
150
|
let response = '';
|
|
151
151
|
|
|
152
152
|
const callback = (token: string) => {
|
|
153
|
-
if (token
|
|
153
|
+
if (stopTokens.includes(token)) return;
|
|
154
154
|
response += token;
|
|
155
155
|
|
|
156
156
|
if (onToken) scheduleOnRN(onToken, token);
|
|
@@ -196,7 +196,7 @@ export async function createLLMChatSession(
|
|
|
196
196
|
// Read and parse tokenizer_config.json
|
|
197
197
|
const tokenizerConfigStr = await RNBlobUtil.fs.readFile(tokenizerConfigPath, 'utf8');
|
|
198
198
|
const tokenizerConfig = parseTokenizerConfig(JSON.parse(tokenizerConfigStr));
|
|
199
|
-
const { chatTemplate,
|
|
199
|
+
const { chatTemplate, stopTokens } = tokenizerConfig;
|
|
200
200
|
|
|
201
201
|
// Prepare chat preprocessor
|
|
202
202
|
const chatPreprocessorConfig = { chatTemplate, modalities, preprocessorConfig, tools };
|
|
@@ -233,7 +233,7 @@ export async function createLLMChatSession(
|
|
|
233
233
|
genConfig?: LLMGenerationConfig
|
|
234
234
|
): Promise<LLMChatTurnResult> => {
|
|
235
235
|
const turnGenConfig = { ...defaultGenerationConfig, ...genConfig };
|
|
236
|
-
const generationOpts = { genConfig: turnGenConfig,
|
|
236
|
+
const generationOpts = { genConfig: turnGenConfig, stopTokens, stopRegex, onToken };
|
|
237
237
|
|
|
238
238
|
const initialCommitted = committed;
|
|
239
239
|
const initialPos = runner.getKVCacheState().pos;
|
|
@@ -14,6 +14,10 @@ export type TokenizerChatConfig = {
|
|
|
14
14
|
readonly chatTemplate: string;
|
|
15
15
|
/** End-of-sequence token string. */
|
|
16
16
|
readonly eosToken: string;
|
|
17
|
+
/**
|
|
18
|
+
* Every terminal token the model may decode into its output.
|
|
19
|
+
*/
|
|
20
|
+
readonly stopTokens: readonly string[];
|
|
17
21
|
};
|
|
18
22
|
|
|
19
23
|
function resolveToken(token: unknown): string | undefined {
|
|
@@ -57,5 +61,11 @@ export function parseTokenizerConfig(config: any): TokenizerChatConfig {
|
|
|
57
61
|
);
|
|
58
62
|
}
|
|
59
63
|
|
|
60
|
-
|
|
64
|
+
const stopTokens = [
|
|
65
|
+
eosToken,
|
|
66
|
+
resolveToken(config.eot_token),
|
|
67
|
+
resolveToken(config.pad_token),
|
|
68
|
+
].filter((token, index, all): token is string => !!token && all.indexOf(token) === index);
|
|
69
|
+
|
|
70
|
+
return { chatTemplate, eosToken, stopTokens };
|
|
61
71
|
}
|
package/src/fetcher/telemetry.ts
CHANGED
|
@@ -10,7 +10,7 @@ const DOWNLOAD_EVENT_ENDPOINT = 'https://ai.swmansion.com/telemetry/downloads/ap
|
|
|
10
10
|
// self-referencing import would need package `exports` support in the consuming
|
|
11
11
|
// bundler to load at all. A wrong value here is analytics noise; a failed
|
|
12
12
|
// import would break the bundle.
|
|
13
|
-
const LIB_VERSION = '0.10.
|
|
13
|
+
const LIB_VERSION = '0.10.3';
|
|
14
14
|
|
|
15
15
|
// Anonymous analytics are on by default; apps opt out via setTelemetryEnabled.
|
|
16
16
|
let telemetryEnabled = true;
|