Skip to content

Commit 25b72b2

Browse files
Fix critical Genie NPU model loading bugs across Kotlin and Flutter SDKs
- Fix Flutter FFI struct mismatch: add missing supports_lora field to RacModelInfoCStruct causing SIGSEGV crash on model registration - Fix Flutter model extraction double-nesting: flatten single extracted subdirectory contents into destDir instead of renaming - Fix Kotlin model extraction: snapshot-based logic to handle archives with non-matching root directories (Genie NPU tar.gz) - Fix Kotlin moveDownloadToFinal: use deleteRecursively() for directory cleanup instead of delete() which silently fails on non-empty dirs - Fix Kotlin ModelFormat enum: align integer values with C++ rac_model_format_t and add QNN_CONTEXT format for Genie models - Add error dialog to Android example app for model load failures Made-with: Cursor
1 parent f12d430 commit 25b72b2

20 files changed

Lines changed: 253 additions & 102 deletions

File tree

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,12 @@ tools/
388388
sdk/runanywhere-react-native/packages/rag/ios/.testlocal
389389

390390

391+
# Python virtual environments
392+
.venv*/
393+
venv*/
394+
__pycache__/
395+
*.pyc
396+
391397
# Node
392398
node_modules/
393399
/tools/

examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/data/ModelList.kt

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -211,19 +211,27 @@ object ModelList {
211211
try {
212212
LlamaCPP.register(priority = 100)
213213
ONNX.register(priority = 100)
214-
Genie.register(priority = 200)
215-
Timber.i("Backends registered")
214+
Timber.i("Core backends registered")
216215
} catch (e: Exception) {
217-
Timber.e(e, "Failed to register backends")
216+
Timber.e(e, "Failed to register core backends")
218217
return
219218
}
220219

221-
val allModels = listOf(
222-
"LLM/STT/TTS" to (llmModels + sttModels + ttsModels),
223-
"Genie NPU" to genieModels(),
224-
"Embedding" to embeddingModels,
225-
"VLM" to vlmModels,
226-
)
220+
var genieRegistered = false
221+
try {
222+
Genie.register(priority = 200)
223+
genieRegistered = true
224+
Timber.i("Genie NPU backend registered")
225+
} catch (e: Exception) {
226+
Timber.w(e, "Genie backend unavailable — continuing without NPU models")
227+
}
228+
229+
val allModels = buildList {
230+
add("LLM/STT/TTS" to (llmModels + sttModels + ttsModels))
231+
if (genieRegistered) add("Genie NPU" to genieModels())
232+
add("Embedding" to embeddingModels)
233+
add("VLM" to vlmModels)
234+
}
227235
for ((label, models) in allModels) {
228236
for (model in models) {
229237
try {

examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelSelectionBottomSheet.kt

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,13 @@ import androidx.compose.material3.Surface
3636
import androidx.compose.material3.Text
3737
import androidx.compose.material3.TextButton
3838
import androidx.compose.material3.rememberModalBottomSheetState
39+
import androidx.compose.material3.AlertDialog
3940
import androidx.compose.runtime.Composable
4041
import androidx.compose.runtime.getValue
42+
import androidx.compose.runtime.mutableStateOf
43+
import androidx.compose.runtime.remember
4144
import androidx.compose.runtime.rememberCoroutineScope
45+
import androidx.compose.runtime.setValue
4246
import androidx.compose.ui.Alignment
4347
import androidx.compose.ui.Modifier
4448
import androidx.compose.ui.draw.clip
@@ -105,6 +109,25 @@ fun ModelSelectionBottomSheet(
105109
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
106110
val deviceStatus = uiState.deviceInfo?.let { toDeviceStatus(it) }
107111
?: DeviceStatus(model = "", chip = "", memory = "", hasNeuralEngine = false)
112+
var errorDialogText by remember { mutableStateOf<String?>(null) }
113+
114+
if (errorDialogText != null) {
115+
AlertDialog(
116+
onDismissRequest = { errorDialogText = null },
117+
title = { Text("Model Load Error") },
118+
text = {
119+
Text(
120+
text = errorDialogText ?: "",
121+
style = MaterialTheme.typography.bodySmall,
122+
)
123+
},
124+
confirmButton = {
125+
TextButton(onClick = { errorDialogText = null }) {
126+
Text("OK")
127+
}
128+
},
129+
)
130+
}
108131

109132
ModalBottomSheet(
110133
onDismissRequest = onDismiss,
@@ -199,12 +222,15 @@ fun ModelSelectionBottomSheet(
199222
delay(500)
200223
attempts++
201224
}
202-
// Only notify success if loading completed WITHOUT errors
203225
val state = viewModel.uiState.value
204226
if (!state.isLoadingModel && state.error == null) {
205227
onModelSelected(model)
228+
onDismiss()
229+
} else if (state.error != null) {
230+
errorDialogText = "Model: ${model.id}\n\nError: ${state.error}"
231+
} else {
232+
errorDialogText = "Model: ${model.id}\n\nTimed out waiting for model to load (${maxAttempts * 500}ms)"
206233
}
207-
onDismiss()
208234
}
209235
}
210236
},

examples/flutter/RunAnywhereAI/android/app/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ android {
5353
signingConfig signingConfigs.debug
5454
}
5555
}
56+
57+
packagingOptions {
58+
pickFirst 'lib/arm64-v8a/libc++_shared.so'
59+
pickFirst 'lib/arm64-v8a/libomp.so'
60+
}
5661
}
5762

5863
flutter {

examples/react-native/RunAnywhereAI/package-lock.json

Lines changed: 14 additions & 21 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/react-native/RunAnywhereAI/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,16 @@
2020
"dependencies": {
2121
"@react-native-async-storage/async-storage": "^2.2.0",
2222
"@react-native-clipboard/clipboard": "^1.16.3",
23+
"@react-native-documents/picker": "^12.0.1",
2324
"@react-navigation/bottom-tabs": "^7.12.0",
2425
"@react-navigation/native": "^7.1.28",
2526
"@react-navigation/native-stack": "^7.12.0",
2627
"@runanywhere/core": "file:../../../sdk/runanywhere-react-native/packages/core",
28+
"@runanywhere/genie": "^0.1.1",
2729
"@runanywhere/llamacpp": "file:../../../sdk/runanywhere-react-native/packages/llamacpp",
2830
"@runanywhere/onnx": "file:../../../sdk/runanywhere-react-native/packages/onnx",
29-
"@runanywhere/genie": "^0.1.1",
3031
"react": "19.2.0",
3132
"react-native": "0.83.1",
32-
"react-native-document-picker": "^9.3.1",
3333
"react-native-fs": "^2.20.0",
3434
"react-native-image-picker": "^8.2.1",
3535
"react-native-live-audio-stream": "^1.1.1",

examples/react-native/RunAnywhereAI/src/screens/RAGScreen.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import {
2828
} from 'react-native';
2929
import { NativeModules } from 'react-native';
3030
import Icon from 'react-native-vector-icons/Ionicons';
31-
import DocumentPicker from 'react-native-document-picker';
31+
import { pick as documentPick } from '@react-native-documents/picker';
3232
import { Colors } from '../theme/colors';
3333
import { Typography, FontWeight } from '../theme/typography';
3434
import { Spacing, Padding, BorderRadius } from '../theme/spacing';
@@ -167,12 +167,11 @@ export const RAGScreen: React.FC = () => {
167167
if (!areModelsReady || !isNitroReady) return;
168168

169169
try {
170-
const result = await DocumentPicker.pickSingle({
171-
type: [DocumentPicker.types.pdf, DocumentPicker.types.plainText, DocumentPicker.types.json],
172-
copyTo: 'cachesDirectory',
170+
const [result] = await documentPick({
171+
type: ['application/pdf', 'text/plain', 'application/json'],
173172
});
174173

175-
const fileUri = result.fileCopyUri || result.uri;
174+
const fileUri = result.uri;
176175
if (!fileUri) return;
177176

178177
setIsLoadingDocument(true);
@@ -205,8 +204,8 @@ export const RAGScreen: React.FC = () => {
205204

206205
setDocumentName(result.name || 'Document');
207206
setIsDocumentLoaded(true);
208-
} catch (err) {
209-
if (DocumentPicker.isCancel(err)) {
207+
} catch (err: any) {
208+
if (err?.code === 'OPERATION_CANCELED') {
210209
return; // User cancelled
211210
}
212211
const msg = err instanceof Error ? err.message : 'Failed to load document';

examples/react-native/RunAnywhereAI/yarn.lock

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,11 @@
12151215
prompts "^2.4.2"
12161216
semver "^7.5.2"
12171217

1218+
"@react-native-documents/picker@^12.0.1":
1219+
version "12.0.1"
1220+
resolved "https://registry.npmjs.org/@react-native-documents/picker/-/picker-12.0.1.tgz"
1221+
integrity sha512-vpJKb4t/5bnxe9+gQl+plJfKrrIsmYwANGhNH2B9E1dS1+6FDBzg4Dwmcq4ueaGfkRKEPJ606mJttVEH1ZKZaA==
1222+
12181223
"@react-native/assets-registry@0.83.1":
12191224
version "0.83.1"
12201225
resolved "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.83.1.tgz"
@@ -5125,13 +5130,6 @@ react-is@^19.1.0:
51255130
resolved "https://registry.npmjs.org/react-is/-/react-is-19.2.4.tgz"
51265131
integrity sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==
51275132

5128-
react-native-document-picker@^9.3.1:
5129-
version "9.3.1"
5130-
resolved "https://registry.npmjs.org/react-native-document-picker/-/react-native-document-picker-9.3.1.tgz"
5131-
integrity sha512-Vcofv9wfB0j67zawFjfq9WQPMMzXxOZL9kBmvWDpjVuEcVK73ndRmlXHlkeFl5ZHVsv4Zb6oZYhqm9u5omJOPA==
5132-
dependencies:
5133-
invariant "^2.2.4"
5134-
51355133
react-native-fs@^2.20.0:
51365134
version "2.20.0"
51375135
resolved "https://registry.npmjs.org/react-native-fs/-/react-native-fs-2.20.0.tgz"
@@ -5194,7 +5192,7 @@ react-native-vision-camera@^4.7.3:
51945192
resolved "https://registry.npmjs.org/react-native-vision-camera/-/react-native-vision-camera-4.7.3.tgz"
51955193
integrity sha512-g1/neOyjSqn1kaAa2FxI/qp5KzNvPcF0bnQw6NntfbxH6tm0+8WFZszlgb5OV+iYlB6lFUztCbDtyz5IpL47OA==
51965194

5197-
react-native@*, "react-native@^0.0.0-0 || >=0.65 <1.0", "react-native@>= 0.61.5", react-native@>=0.70.0, react-native@>=0.74.0, react-native@0.83.1:
5195+
react-native@*, "react-native@^0.0.0-0 || >=0.65 <1.0", "react-native@>= 0.61.5", react-native@>=0.70.0, react-native@>=0.74.0, react-native@>=0.79.0, react-native@0.83.1:
51985196
version "0.83.1"
51995197
resolved "https://registry.npmjs.org/react-native/-/react-native-0.83.1.tgz"
52005198
integrity sha512-mL1q5HPq5cWseVhWRLl+Fwvi5z1UO+3vGOpjr+sHFwcUletPRZ5Kv+d0tUfqHmvi73/53NjlQqX1Pyn4GguUfA==

sdk/runanywhere-flutter/packages/runanywhere/android/binary_config.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
ext {
1212
// Set this to true for local development/testing
1313
// Set to false for production builds (downloads from GitHub releases)
14-
testLocal = false
14+
testLocal = true
1515

1616
// =============================================================================
1717
// Version Configuration (MUST match Swift Package.swift and Kotlin build.gradle.kts)

0 commit comments

Comments
 (0)