Skip to content

Commit b938284

Browse files
committed
code cleanup
1 parent edec3db commit b938284

29 files changed

Lines changed: 285 additions & 622 deletions
-4.93 MB
Binary file not shown.

‎apps/llm/app/llm/index.tsx‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,13 @@ function LLMScreen() {
4242
const { bottom } = useSafeAreaInsets();
4343
const [isTextInputFocused, setIsTextInputFocused] = useState(false);
4444
const [userInput, setUserInput] = useState('');
45+
const [selectedModel, setSelectedModel] = useState<LLMModelSources>(
46+
models.llm.lfm2_5_1_2b_instruct()
47+
);
4548
const textInputRef = useRef<TextInput>(null);
4649
const { setGlobalGenerating } = useContext(GeneratingContext);
4750

48-
let llm = Object();
49-
51+
const llm = useLLM({ model: selectedModel });
5052
const tokenCount = llm.isReady ? llm.getGeneratedTokenCount() : 0;
5153
const { stats, onMessageSend } = useLLMStats(
5254
llm.response,
@@ -110,6 +112,13 @@ function LLMScreen() {
110112
/>
111113
</View>
112114
)}
115+
116+
<ModelPicker
117+
models={LLM_MODELS}
118+
selectedModel={selectedModel}
119+
onSelect={(m) => setSelectedModel(m)}
120+
disabled={llm.isGenerating}
121+
/>
113122
<StatsBar stats={stats} />
114123
<View
115124
style={[

‎apps/llm/components/llmModels.ts‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
LLAMA3_2_1B_QLORA,
44
LLAMA3_2_3B_QLORA,
55
LLMProps,
6-
GEMMA4_E2B_QUANTIZED,
76
} from 'react-native-executorch';
87
import { ModelOption } from './ModelPicker';
98
const llm = models.llm;
@@ -12,7 +11,7 @@ export type LLMModelSources = LLMProps['model'];
1211

1312
export const LLM_MODELS: ModelOption<LLMModelSources>[] = [
1413
// Gemma4
15-
{ label: 'Gemma4 e2b Quantized', value: GEMMA4_E2B_QUANTIZED },
14+
{ label: 'Gemma4 E2B', value: llm.gemma4_e2b() },
1615
// Llama 3.2
1716
{
1817
label: 'Llama 3.2 1B',

‎packages/react-native-executorch/common/rnexecutorch/host_objects/JsiConversions.h‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include <rnexecutorch/metaprogramming/TypeConcepts.h>
1919
#include <rnexecutorch/models/instance_segmentation/Types.h>
20+
#include <rnexecutorch/models/llm/Types.h>
2021
#include <rnexecutorch/models/object_detection/Constants.h>
2122
#include <rnexecutorch/models/object_detection/Types.h>
2223
#include <rnexecutorch/models/ocr/Types.h>
@@ -318,6 +319,31 @@ getValue<std::span<uint64_t>>(const jsi::Value &val, jsi::Runtime &runtime) {
318319
return getTypedArrayAsSpan<uint64_t>(val, runtime);
319320
}
320321

322+
template <typename T>
323+
inline void assignIfPresent(T &field, const jsi::Object &obj,
324+
jsi::Runtime &runtime, const char *name) {
325+
if (!obj.hasProperty(runtime, name))
326+
return;
327+
jsi::Value v = obj.getProperty(runtime, name);
328+
if (v.isUndefined() || v.isNull())
329+
return;
330+
field = getValue<T>(v, runtime);
331+
}
332+
333+
template <>
334+
inline models::llm::MultimodalInputs
335+
getValue<models::llm::MultimodalInputs>(const jsi::Value &val,
336+
jsi::Runtime &runtime) {
337+
models::llm::MultimodalInputs multimodalInputs;
338+
jsi::Object obj = val.asObject(runtime);
339+
assignIfPresent(multimodalInputs.imagePaths, obj, runtime, "imagePaths");
340+
assignIfPresent(multimodalInputs.imageToken, obj, runtime, "imageToken");
341+
assignIfPresent(multimodalInputs.audioWaveforms, obj, runtime,
342+
"audioWaveforms");
343+
assignIfPresent(multimodalInputs.audioToken, obj, runtime, "audioToken");
344+
return multimodalInputs;
345+
}
346+
321347
// Conversion from C++ types to jsi --------------------------------------------
322348

323349
// Implementation functions might return any type, but in a promise we can only

‎packages/react-native-executorch/common/rnexecutorch/models/llm/LLM.cpp‎

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,9 @@ std::string LLM::generate(std::string input,
7676
return output;
7777
}
7878

79-
std::string LLM::generateMultimodal(
80-
std::string prompt, std::shared_ptr<jsi::Function> callback,
81-
std::vector<std::string> imagePaths, std::string imageToken,
82-
std::vector<std::vector<float>> audioWaveforms, std::string audioToken) {
79+
std::string LLM::generateMultimodal(std::string prompt,
80+
std::shared_ptr<jsi::Function> callback,
81+
MultimodalInputs mutlimodalInputs) {
8382
if (!runner_ || !runner_->is_loaded()) {
8483
throw RnExecutorchError(RnExecutorchErrorCode::ModuleNotLoaded,
8584
"Runner is not loaded");
@@ -88,7 +87,8 @@ std::string LLM::generateMultimodal(
8887
throw RnExecutorchError(RnExecutorchErrorCode::InvalidUserInput,
8988
"This model does not support multimodal input.");
9089
}
91-
if (imageToken.empty() && audioToken.empty()) {
90+
if (mutlimodalInputs.imageToken.empty() &&
91+
mutlimodalInputs.audioToken.empty()) {
9292
throw RnExecutorchError(
9393
RnExecutorchErrorCode::InvalidUserInput,
9494
"At least one of imageToken/audioToken must be non-empty");
@@ -98,12 +98,13 @@ std::string LLM::generateMultimodal(
9898
// so that image/audio placeholders can be freely interleaved in the prompt.
9999
std::vector<llm::MultimodalInput> inputs;
100100
size_t imageIdx = 0, audioIdx = 0, pos = 0;
101-
constexpr int32_t kAudioSampleRate = 16000;
102101
while (pos < prompt.size()) {
103-
size_t imgAt =
104-
imageToken.empty() ? std::string::npos : prompt.find(imageToken, pos);
105-
size_t audAt =
106-
audioToken.empty() ? std::string::npos : prompt.find(audioToken, pos);
102+
size_t imgAt = mutlimodalInputs.imageToken.empty()
103+
? std::string::npos
104+
: prompt.find(mutlimodalInputs.imageToken, pos);
105+
size_t audAt = mutlimodalInputs.audioToken.empty()
106+
? std::string::npos
107+
: prompt.find(mutlimodalInputs.audioToken, pos);
107108
if (imgAt == std::string::npos && audAt == std::string::npos) {
108109
inputs.push_back(llm::make_text_input(prompt.substr(pos)));
109110
break;
@@ -115,25 +116,27 @@ std::string LLM::generateMultimodal(
115116
inputs.push_back(llm::make_text_input(prompt.substr(pos, at - pos)));
116117
}
117118
if (imageFirst) {
118-
if (imageIdx >= imagePaths.size()) {
119+
if (imageIdx >= mutlimodalInputs.imagePaths.size()) {
119120
throw RnExecutorchError(RnExecutorchErrorCode::InvalidUserInput,
120-
"More '" + imageToken +
121+
"More '" + mutlimodalInputs.imageToken +
121122
"' placeholders than image paths");
122123
}
123-
inputs.push_back(llm::make_image_input(imagePaths[imageIdx++]));
124-
pos = at + imageToken.size();
124+
inputs.push_back(
125+
llm::make_image_input(mutlimodalInputs.imagePaths[imageIdx++]));
126+
pos = at + mutlimodalInputs.imageToken.size();
125127
} else {
126-
if (audioIdx >= audioWaveforms.size()) {
128+
if (audioIdx >= mutlimodalInputs.audioWaveforms.size()) {
127129
throw RnExecutorchError(RnExecutorchErrorCode::InvalidUserInput,
128-
"More '" + audioToken +
130+
"More '" + mutlimodalInputs.audioToken +
129131
"' placeholders than audio waveforms");
130132
}
131133
inputs.push_back(llm::make_audio_input(
132-
std::move(audioWaveforms[audioIdx++]), kAudioSampleRate));
133-
pos = at + audioToken.size();
134+
std::move(mutlimodalInputs.audioWaveforms[audioIdx++])));
135+
pos = at + mutlimodalInputs.audioToken.size();
134136
}
135137
}
136-
if (imageIdx < imagePaths.size() || audioIdx < audioWaveforms.size()) {
138+
if (imageIdx < mutlimodalInputs.imagePaths.size() ||
139+
audioIdx < mutlimodalInputs.audioWaveforms.size()) {
137140
throw RnExecutorchError(
138141
RnExecutorchErrorCode::InvalidUserInput,
139142
"More image/audio paths provided than placeholders in prompt");

‎packages/react-native-executorch/common/rnexecutorch/models/llm/LLM.h‎

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <ReactCommon/CallInvoker.h>
88
#include <jsi/jsi.h>
99
#include <rnexecutorch/models/BaseModel.h>
10+
#include <rnexecutorch/models/llm/Types.h>
1011
#include <runner/base_llm_runner.h>
1112

1213
namespace rnexecutorch {
@@ -27,11 +28,9 @@ class LLM : public BaseModel {
2728
// The prompt is scanned for `imageToken` and/or `audioToken` placeholders;
2829
// each placeholder consumes the next entry from its respective vector in
2930
// order. Either set of paths/waveforms/token may be empty.
30-
std::string generateMultimodal(
31-
std::string prompt, std::shared_ptr<jsi::Function> callback,
32-
std::vector<std::string> imagePaths = {}, std::string imageToken = "",
33-
std::vector<std::vector<float>> audioWaveforms = {},
34-
std::string audioToken = "");
31+
std::string generateMultimodal(std::string prompt,
32+
std::shared_ptr<jsi::Function> callback,
33+
MultimodalInputs mutlimodalInputs = {});
3534

3635
void interrupt();
3736
void reset();
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <vector>
5+
6+
namespace rnexecutorch::models::llm {
7+
struct MultimodalInputs {
8+
std::vector<std::string> imagePaths;
9+
std::string imageToken;
10+
std::vector<std::vector<float>> audioWaveforms;
11+
std::string audioToken;
12+
};
13+
using Token = uint64_t;
14+
15+
} // namespace rnexecutorch::models::llm

‎packages/react-native-executorch/common/runner/base_llm_runner.cpp‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ BaseLLMRunner::BaseLLMRunner(std::unique_ptr<Module> module,
1818
tokenizer_(std::make_unique<tokenizers::HFTokenizer>()),
1919
metadata_({
2020
{kEnableDynamicShape, false},
21-
{kMaxSeqLen, 2048},
22-
{kMaxContextLen, 2048},
21+
{kMaxSeqLen, 128},
22+
{kMaxContextLen, 128},
2323
{kUseKVCache, true},
2424
}) {}
2525

‎packages/react-native-executorch/common/runner/encoders/audio_encoder.cpp‎

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
#include "audio_encoder.h"
33

44
#include <rnexecutorch/Error.h>
5-
#include <rnexecutorch/Log.h>
65
#include <runner/constants.h>
76

87
#include <executorch/extension/tensor/tensor.h>
@@ -21,12 +20,7 @@ using ::executorch::runtime::EValue;
2120
using ::executorch::runtime::Result;
2221

2322
namespace {
24-
// Matches AUDIO_SAMPLES_PER_BLOCK in gemma_export/experiments_vulkan/
25-
// op_bisect/iter201_mm_4method_dynaudio_prefill2048_export.py.
26-
// The PTE's audio_samples dim was exported as `7680 * audio_blocks`.
2723
constexpr int32_t kSamplesPerBlock = 7680;
28-
// k ∈ [kAudioBlockKMin, kAudioBlockKMax] from MODEL_INTERFACE.md §6.
29-
// k=62 == 29.76 s @ 16 kHz is the SDPA mask + rel-shift bake point.
3024
constexpr int64_t kAudioBlockKMin = 1;
3125
constexpr int64_t kAudioBlockKMax = 62;
3226
} // namespace
@@ -55,7 +49,9 @@ bool AudioEncoder::is_loaded() const noexcept {
5549
return module_->is_method_loaded(kAudioEncoderMethod);
5650
}
5751

58-
int32_t AudioEncoder::encoderTokenCount() const { return last_token_count_; }
52+
int32_t AudioEncoder::encoderTokenCount() const noexcept {
53+
return last_token_count_;
54+
}
5955

6056
Result<EValue> AudioEncoder::encode(const MultimodalInput &input) {
6157
if (!is_loaded()) {
@@ -68,9 +64,6 @@ Result<EValue> AudioEncoder::encode(const MultimodalInput &input) {
6864
const auto &wav = input.get_audio();
6965
ET_CHECK_OR_RETURN_ERROR(!wav.samples.empty(), InvalidArgument,
7066
"AudioEncoder: empty waveform");
71-
ET_CHECK_OR_RETURN_ERROR(
72-
wav.sample_rate == 16000, InvalidArgument,
73-
"AudioEncoder: expected 16000 Hz waveform, got %d Hz", wav.sample_rate);
7467

7568
const int64_t n_valid = static_cast<int64_t>(wav.samples.size());
7669
const int64_t k_blocks = (n_valid + kSamplesPerBlock - 1) / kSamplesPerBlock;
@@ -116,10 +109,6 @@ Result<EValue> AudioEncoder::encode(const MultimodalInput &input) {
116109
"audio_encoder output rank=%zd, expected 3",
117110
audio_tensor.dim());
118111
last_token_count_ = static_cast<int32_t>(audio_tensor.size(1));
119-
rnexecutorch::log(rnexecutorch::LOG_LEVEL::Info,
120-
"AudioEncoder: valid_samples=", n_valid,
121-
" padded_samples=", n_padded, " k_blocks=", k_blocks,
122-
" audio_tokens=", last_token_count_);
123112
return exec_result[0];
124113
}
125114

‎packages/react-native-executorch/common/runner/encoders/audio_encoder.h‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class AudioEncoder : public IEncoder {
2828
encode(const MultimodalInput &input) override;
2929
// Number of audio embedding tokens produced per encode() call. 0 until first
3030
// encode, since Gemma4's audio_encoder has a dynamic T dim.
31-
int32_t encoderTokenCount() const override;
31+
int32_t encoderTokenCount() const noexcept override;
3232

3333
private:
3434
::executorch::extension::Module *module_;

0 commit comments

Comments
 (0)