Skip to content

Commit c167ea7

Browse files
Siddhesh2377claude
andcommitted
Fix bugs across Kotlin, Flutter, and React Native SDKs
Genie NPU fixes (all 3 SDKs): - downloadUrl() now accepts quant parameter (default w4a16) for w8a16 models - Fixed model slug mismatch (llama-v3.2 → llama3.2) - Added missing models: SEA-LION v3.5 8B, Qwen 2.5 7B (w8a16) Kotlin SDK: - SecureStorageFactory.create() race condition → @synchronized - SDKLogger destination iteration thread safety → .toList() snapshot - isNativeInitialized() now calls RunAnywhereBridge.racIsInitialized() - Removed runBlocking ANR risk → suspend chain with Mutex React Native SDK: - Memory leak: added rac_llm_result_free() in generate() - cancelDownload() now actually stops RNFS download via jobId tracking - CMakeLists RAC_INCLUDE_DIR fixed to jniLibs/include/ (3 files) - PlatformAdapterBridge.getChipName() uses Build.SOC_MODEL (API 31+) - extractIntValue/extractFloatValue crash guard (try/catch) - transcribeFile returns JSON matching transcribe() + fixed JSON escaping - WAV parser: memcpy instead of reinterpret_cast (ARM safe) - lastError_ protected with errorMutex_ - deleteModel handles all file extensions - getModelInfo handles empty object as null - Fixed VLM cross-package import error Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2463944 commit c167ea7

21 files changed

Lines changed: 146 additions & 54 deletions

File tree

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

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ object ModelList {
137137
val name: String,
138138
val memoryRequirement: Long,
139139
val supportedChips: Set<NPUChip>,
140+
val quant: String = "w4a16",
140141
)
141142

142143
private val genieModelDefs = listOf(
@@ -147,11 +148,24 @@ object ModelList {
147148
supportedChips = setOf(NPUChip.SNAPDRAGON_8_ELITE_GEN5),
148149
),
149150
GenieModelDef(
150-
slug = "llama-v3.2-1b-instruct",
151+
slug = "llama3.2-1b-instruct",
151152
name = "Llama 3.2 1B Instruct",
152153
memoryRequirement = 1_200_000_000,
153154
supportedChips = setOf(NPUChip.SNAPDRAGON_8_ELITE, NPUChip.SNAPDRAGON_8_ELITE_GEN5),
154155
),
156+
GenieModelDef(
157+
slug = "sea-lion3.5-8b-instruct",
158+
name = "SEA-LION v3.5 8B Instruct",
159+
memoryRequirement = 4_800_000_000,
160+
supportedChips = setOf(NPUChip.SNAPDRAGON_8_ELITE, NPUChip.SNAPDRAGON_8_ELITE_GEN5),
161+
),
162+
GenieModelDef(
163+
slug = "qwen2.5-7b-instruct",
164+
name = "Qwen 2.5 7B Instruct",
165+
memoryRequirement = 4_200_000_000,
166+
supportedChips = setOf(NPUChip.SNAPDRAGON_8_ELITE),
167+
quant = "w8a16",
168+
),
155169
)
156170

157171
private fun genieModels(): List<AppModel> {
@@ -162,7 +176,7 @@ object ModelList {
162176
AppModel(
163177
id = "${def.slug}-npu-${chip.identifier}",
164178
name = "${def.name} (NPU - ${chip.displayName})",
165-
url = chip.downloadUrl(def.slug),
179+
url = chip.downloadUrl(def.slug, def.quant),
166180
framework = InferenceFramework.GENIE,
167181
category = ModelCategory.LANGUAGE,
168182
memoryRequirement = def.memoryRequirement,

examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -215,17 +215,20 @@ class _RunAnywhereAIAppState extends State<RunAnywhereAIApp> {
215215
// Models with per-chip availability
216216
const genieModels = [
217217
// Qwen3 4B — Gen 5 only
218-
(slug: 'qwen3-4b', name: 'Qwen3 4B', mem: 2800000000, chips: {NPUChip.snapdragon8EliteGen5}),
218+
(slug: 'qwen3-4b', name: 'Qwen3 4B', mem: 2800000000, quant: 'w4a16', chips: {NPUChip.snapdragon8EliteGen5}),
219219
// Llama 3.2 1B Instruct — both chips
220-
// TODO: Remove snapdragon7sGen3Test before merging
221-
(slug: 'llama-v3.2-1b-instruct', name: 'Llama 3.2 1B Instruct', mem: 1200000000, chips: {NPUChip.snapdragon8Elite, NPUChip.snapdragon8EliteGen5, NPUChip.snapdragon7sGen3Test}),
220+
(slug: 'llama3.2-1b-instruct', name: 'Llama 3.2 1B Instruct', mem: 1200000000, quant: 'w4a16', chips: {NPUChip.snapdragon8Elite, NPUChip.snapdragon8EliteGen5}),
221+
// SEA-LION v3.5 8B Instruct — both chips
222+
(slug: 'sea-lion3.5-8b-instruct', name: 'SEA-LION v3.5 8B Instruct', mem: 4800000000, quant: 'w4a16', chips: {NPUChip.snapdragon8Elite, NPUChip.snapdragon8EliteGen5}),
223+
// Qwen 2.5 7B Instruct — 8elite only, w8a16 quant
224+
(slug: 'qwen2.5-7b-instruct', name: 'Qwen 2.5 7B Instruct', mem: 4200000000, quant: 'w8a16', chips: {NPUChip.snapdragon8Elite}),
222225
];
223226
for (final m in genieModels) {
224227
if (m.chips.contains(chip)) {
225228
Genie.addModel(
226229
id: '${m.slug}-npu-${chip.identifier}',
227230
name: '${m.name} (NPU - ${chip.displayName})',
228-
url: chip.downloadUrl(m.slug),
231+
url: chip.downloadUrl(m.slug, quant: m.quant),
229232
memoryRequirement: m.mem,
230233
);
231234
}

examples/react-native/RunAnywhereAI/App.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,16 @@ async function registerModulesAndModels(): Promise<void> {
256256
name: string;
257257
mem: number;
258258
chips: string[];
259+
quant?: string;
259260
}> = [
260261
// Qwen3 4B — Gen 5 only
261262
{ slug: 'qwen3-4b', name: 'Qwen3 4B', mem: 2_800_000_000, chips: ['8elite-gen5'] },
262263
// Llama 3.2 1B Instruct — both chips
263-
{ slug: 'llama-v3.2-1b-instruct', name: 'Llama 3.2 1B Instruct', mem: 1_200_000_000, chips: ['8elite', '8elite-gen5'] },
264+
{ slug: 'llama3.2-1b-instruct', name: 'Llama 3.2 1B Instruct', mem: 1_200_000_000, chips: ['8elite', '8elite-gen5'] },
265+
// SEA-LION v3.5 8B Instruct — both chips
266+
{ slug: 'sea-lion3.5-8b-instruct', name: 'SEA-LION v3.5 8B Instruct', mem: 4_800_000_000, chips: ['8elite', '8elite-gen5'] },
267+
// Qwen 2.5 7B Instruct — 8elite only, w8a16 quant
268+
{ slug: 'qwen2.5-7b-instruct', name: 'Qwen 2.5 7B Instruct', mem: 4_200_000_000, chips: ['8elite'], quant: 'w8a16' },
264269
];
265270

266271
const registrations = genieModels
@@ -269,7 +274,7 @@ async function registerModulesAndModels(): Promise<void> {
269274
RunAnywhere.registerModel({
270275
id: `${m.slug}-npu-${chip.identifier}`,
271276
name: `${m.name} (NPU - ${chip.displayName})`,
272-
url: getNPUDownloadUrl(chip, m.slug),
277+
url: getNPUDownloadUrl(chip, m.slug, m.quant),
273278
framework: LLMFramework.Genie,
274279
memoryRequirement: m.mem,
275280
}),

sdk/runanywhere-flutter/packages/runanywhere/lib/core/types/npu_chip.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ enum NPUChip {
2929
/// Build a HuggingFace download URL for this chip.
3030
/// [modelSlug] is the model slug (e.g. "qwen3-4b") → produces
3131
/// "qwen3-4b-genie-w4a16-8elite-gen5.tar.gz"
32-
String downloadUrl(String modelSlug) =>
33-
'$baseUrl$modelSlug-genie-w4a16-$npuSuffix.tar.gz';
32+
/// [quant] is the quantization format (e.g. "w4a16", "w8a16"). Defaults to "w4a16".
33+
String downloadUrl(String modelSlug, {String quant = 'w4a16'}) =>
34+
'$baseUrl$modelSlug-genie-$quant-$npuSuffix.tar.gz';
3435

3536
/// Match an NPU chip from a SoC model string (e.g. "SM8750").
3637
/// Returns null if the SoC is not a supported NPU chipset.

sdk/runanywhere-kotlin/src/androidMain/kotlin/com/runanywhere/sdk/security/SecureStorage.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class AndroidSecureStorage private constructor(
3939
/**
4040
* Create secure storage instance for Android
4141
*/
42+
@Synchronized
4243
fun create(identifier: String): AndroidSecureStorage {
4344
val appContext =
4445
context

sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/core/types/NPUChip.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,10 @@ enum class NPUChip(
2727
* Build a HuggingFace download URL for this chip.
2828
* @param modelSlug Model slug (e.g. "qwen3-4b") → produces
2929
* "qwen3-4b-genie-w4a16-8elite-gen5.tar.gz"
30+
* @param quant Quantization format (e.g. "w4a16", "w8a16"). Defaults to "w4a16".
3031
*/
31-
fun downloadUrl(modelSlug: String): String =
32-
"${BASE_URL}${modelSlug}-genie-w4a16-${npuSuffix}.tar.gz"
32+
fun downloadUrl(modelSlug: String, quant: String = "w4a16"): String =
33+
"${BASE_URL}${modelSlug}-genie-${quant}-${npuSuffix}.tar.gz"
3334

3435
companion object {
3536
/** Base URL for NPU model downloads on HuggingFace. */

sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/foundation/SDKLogger.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,8 +332,8 @@ object Logging {
332332
// Forward to runanywhere-commons bridge if set
333333
commonsLogBridge?.invoke(entry)
334334

335-
// Write to all registered destinations
336-
for (destination in _destinations) {
335+
// Write to all registered destinations (snapshot to avoid concurrent modification)
336+
for (destination in _destinations.toList()) {
337337
if (destination.isAvailable) {
338338
destination.write(entry)
339339
}
@@ -358,6 +358,7 @@ object Logging {
358358
/**
359359
* Add a log destination (non-suspending version).
360360
*/
361+
@Synchronized
361362
fun addDestinationSync(destination: LogDestination) {
362363
if (_destinations.none { it.identifier == destination.identifier }) {
363364
_destinations.add(destination)

sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/public/RunAnywhere.kt

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import com.runanywhere.sdk.foundation.LogLevel
1414
import com.runanywhere.sdk.foundation.SDKLogger
1515
import com.runanywhere.sdk.public.events.EventBus
1616
import com.runanywhere.sdk.utils.SDKConstants
17+
import kotlinx.coroutines.sync.Mutex
18+
import kotlinx.coroutines.sync.withLock
1719

1820
// ═══════════════════════════════════════════════════════════════════════════
1921
// SDK INITIALIZATION FLOW (Two-Phase Pattern)
@@ -91,6 +93,7 @@ object RunAnywhere {
9193
private var _areServicesReady: Boolean = false
9294

9395
private val lock = Any()
96+
private val servicesMutex = Mutex()
9497

9598
// ═══════════════════════════════════════════════════════════════════════════
9699
// MARK: - Public Properties
@@ -240,7 +243,7 @@ object RunAnywhere {
240243
return
241244
}
242245

243-
synchronized(lock) {
246+
servicesMutex.withLock {
244247
if (_areServicesReady) {
245248
return
246249
}
@@ -334,7 +337,7 @@ object RunAnywhere {
334337
* Initialize CppBridge services (Phase 2)
335338
* Implementation is in jvmAndroidMain via expect/actual
336339
*/
337-
private fun initializeCppBridgeServices() {
340+
private suspend fun initializeCppBridgeServices() {
338341
logger.debug("CppBridge services initialization requested")
339342
initializePlatformBridgeServices()
340343
}
@@ -367,7 +370,7 @@ internal expect fun initializePlatformBridge(environment: SDKEnvironment, apiKey
367370
* Initialize platform-specific bridge services (Phase 2).
368371
* On JVM/Android, this calls CppBridge.initializeServices().
369372
*/
370-
internal expect fun initializePlatformBridgeServices()
373+
internal expect suspend fun initializePlatformBridgeServices()
371374

372375
/**
373376
* Shutdown platform-specific bridge.

sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/CppBridge.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -558,8 +558,12 @@ object CppBridge {
558558
* @return true if rac_is_initialized() returns true
559559
*/
560560
fun isNativeInitialized(): Boolean {
561-
// TODO: Call rac_is_initialized()
562-
return _isInitialized
561+
if (!_isInitialized || !isNativeLibraryLoaded) return false
562+
return try {
563+
RunAnywhereBridge.racIsInitialized()
564+
} catch (_: Exception) {
565+
_isInitialized
566+
}
563567
}
564568

565569
// =============================================================================

sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/PlatformBridge.kt

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ package com.runanywhere.sdk.public
1111
import com.runanywhere.sdk.foundation.SDKLogger
1212
import com.runanywhere.sdk.foundation.bridge.CppBridge
1313
import com.runanywhere.sdk.foundation.bridge.extensions.CppBridgeTelemetry
14-
import kotlinx.coroutines.runBlocking
1514

1615
private const val TAG = "PlatformBridge"
1716
private val logger = SDKLogger(TAG)
@@ -49,15 +48,9 @@ internal actual fun initializePlatformBridge(environment: SDKEnvironment, apiKey
4948
* Initialize CppBridge services (Phase 2).
5049
* This includes model assignment, platform services, and device registration.
5150
*/
52-
internal actual fun initializePlatformBridgeServices() {
51+
internal actual suspend fun initializePlatformBridgeServices() {
5352
logger.info("Initializing CppBridge services...")
54-
55-
// Use runBlocking to call the suspend function
56-
// This is safe because services initialization is typically called once
57-
runBlocking {
58-
CppBridge.initializeServices()
59-
}
60-
53+
CppBridge.initializeServices()
6154
logger.info("CppBridge services initialization complete")
6255
}
6356

0 commit comments

Comments
 (0)