Skip to content

Add Genie NPU backend support (Qualcomm Snapdragon)#462

Open
shubhammalhotra28 wants to merge 4 commits intomainfrom
smonga/genie_support_v2
Open

Add Genie NPU backend support (Qualcomm Snapdragon)#462
shubhammalhotra28 wants to merge 4 commits intomainfrom
smonga/genie_support_v2

Conversation

@shubhammalhotra28
Copy link
Copy Markdown
Contributor

@shubhammalhotra28 shubhammalhotra28 commented Mar 20, 2026

Summary

  • Adds Genie NPU backend support for Qualcomm Snapdragon devices
  • Includes SM8850 (Snapdragon 8 Elite Gen 5 / S26) support via libQnnHtpV81.so
  • 6 critical bug fixes for model extraction and loading across Kotlin and Flutter SDKs
  • Re-created from Add Genie NPU backend support (Qualcomm Snapdragon) #446 (which was accidentally merged and reverted)

New Features

  • Genie NPU backend integration across Kotlin, Flutter, and React Native SDKs
  • getChip() API for NPU chip detection
  • Flutter runanywhere_genie package
  • SM8850 V81 HTP runtime support

Bug Fixes

  1. Flutter FFI struct mismatch (SIGSEGV crash fix)
  2. Flutter model extraction double-nesting
  3. Kotlin model extraction for non-matching root directories
  4. Kotlin directory cleanup with deleteRecursively()
  5. Kotlin ModelFormat enum alignment with C++
  6. Error dialog for model load failures in example app

Test Plan

  • Kotlin APK on S25 (SM8750) - regression test
  • Kotlin APK on S26 (SM8850) - new V81 support test
  • Flutter APK on S25 - regression test
  • Flutter APK on S26 - new V81 support test

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Qualcomm Genie NPU backend support across Flutter, Kotlin, and React Native SDKs with device chip detection and NPU-accelerated model inference.
    • Enhanced LoRA adapter validation with GGUF magic verification and tensor matching checks.
    • Added new comprehensive SDK documentation for Flutter, Kotlin, and React Native platforms.
  • Bug Fixes

    • Improved archive extraction with intelligent path flattening and directory handling.
    • Fixed numeric JSON parsing robustness with fallback defaults.
    • Enhanced thread safety for concurrent operations.
  • Documentation

    • Added Flutter SDK guide with architecture and API reference.
    • Added Kotlin Multiplatform SDK documentation.
    • Added React Native SDK guide with platform-specific implementation details.

…y config

- Add libQnnHtpV81.so to Kotlin example jniLibs for S26 testing
- Update Flutter binary_config.gradle to reference genie-v0.3.0 release

Made-with: Cursor
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 20, 2026

Too many files changed for review. (129 files found, 100 file limit)

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 711b9bc6-a4ad-4129-a903-b51b6161578f

📥 Commits

Reviewing files that changed from the base of the PR and between c867708 and 1e28a1f.

📒 Files selected for processing (5)
  • examples/android/RunAnywhereAI/gradle.properties
  • examples/react-native/RunAnywhereAI/android/app/build.gradle
  • gradle.properties
  • sdk/runanywhere-flutter/packages/runanywhere_genie/android/binary_config.gradle
  • sdk/runanywhere-kotlin/gradle.properties
✅ Files skipped from review due to trivial changes (3)
  • gradle.properties
  • examples/android/RunAnywhereAI/gradle.properties
  • sdk/runanywhere-kotlin/gradle.properties
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/react-native/RunAnywhereAI/android/app/build.gradle
  • sdk/runanywhere-flutter/packages/runanywhere_genie/android/binary_config.gradle

📝 Walkthrough

Walkthrough

This pull request introduces Qualcomm Genie NPU backend support across the RunAnywhere SDK family (Flutter, Kotlin, React Native, and commons). It adds device NPU chip detection APIs, a new Genie inference framework constant, enhanced LoRA adapter validation in the llamacpp backend, refactored archive extraction using system tools, and comprehensive documentation for three SDKs. The change includes new example app integrations, UI updates, and build configurations to support Genie NPU-accelerated models on Android.

Changes

Cohort / File(s) Summary
Configuration & Build
.gitignore, lefthook.yml, gradle.properties, examples/android/RunAnywhereAI/gradle.properties, sdk/runanywhere-kotlin/gradle.properties
Added Python development artifact patterns to .gitignore, added Lefthook config template, and switched runanywhere.testLocal from true to false across multiple Gradle configurations to use pre-built JNI libraries instead of local builds.
IDE & VCS Configuration
.idea/vcs.xml, sdk/runanywhere-kotlin/settings.gradle.kts
Updated Git VCS mappings for build dependency source directories and added inline documentation for closed-source Genie AAR module.
Documentation
docs/impl/lora_adapter_support.md, docs/sdks/flutter-sdk.md, docs/sdks/kotlin-sdk.md, docs/sdks/react-native-sdk.md
Updated LoRA adapter documentation to target llama.cpp b8201 with batch adapter API and enhanced validation; added comprehensive SDK documentation for Flutter, Kotlin, and React Native covering installation, quick start, architecture, API reference, and Genie NPU model support.
C++ Commons: Framework & Model Types
sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_types.h, sdk/runanywhere-commons/src/infrastructure/model_management/model_types.cpp, sdk/runanywhere-commons/src/infrastructure/telemetry/telemetry_manager.cpp
Added RAC_MODEL_FORMAT_QNN_CONTEXT = 5 and RAC_FRAMEWORK_GENIE = 10 enums; extended framework helpers (category, LLM support, directory-based models, format support, display names, analytics keys).
C++ Commons: Model Management
sdk/runanywhere-commons/src/infrastructure/model_management/model_paths.cpp, sdk/runanywhere-commons/src/infrastructure/model_management/model_registry.cpp, sdk/runanywhere-commons/src/features/diffusion/diffusion_json.cpp, sdk/runanywhere-commons/src/infrastructure/model_management/model_assignment.cpp
Updated framework string parsing to recognize "genie", "qnn_genie", and "Genie" variants mapping to RAC_FRAMEWORK_GENIE; extended model discovery to scan Genie frameworks.
C++ Commons: LLM & Backend
sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt, sdk/runanywhere-commons/src/backends/llamacpp/llamacpp_backend.cpp, sdk/runanywhere-commons/src/backends/llamacpp/rac_llm_llamacpp.cpp, sdk/runanywhere-commons/src/features/llm/llm_component.cpp, sdk/runanywhere-commons/src/features/llm/rac_llm_service.cpp
Enhanced LoRA adapter validation (GGUF magic check, tensor match counting, metadata logging, pre-generation verification); added adapter path in error messages; exposed llama-adapter.h in public includes; added debug logging in LLM service creation and adapter compatibility checking.
C++ Commons: Service Registry
sdk/runanywhere-commons/src/infrastructure/registry/service_registry.cpp
Added debug logging around provider iteration during rac_service_create to track provider checks, capability matching, and creation outcomes.
Flutter SDK Core
sdk/runanywhere-flutter/packages/runanywhere/lib/core/types/model_types.dart, sdk/runanywhere-flutter/packages/runanywhere/lib/core/types/npu_chip.dart, sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart, sdk/runanywhere-flutter/packages/runanywhere/pubspec.yaml, sdk/runanywhere-flutter/packages/runanywhere/CHANGELOG.md
Added InferenceFramework.genie enum; introduced NPUChip enum with downloadUrl() and fromSocModel() methods; refactored archive extraction to use system tar/unzip instead of Dart archive package; updated version to 0.17.0 and removed archive dependency.
Flutter SDK: FFI & Native Bridge
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_model_paths.dart, sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_model_registry.dart, sdk/runanywhere-flutter/packages/runanywhere/lib/native/type_conversions/model_types_cpp_bridge.dart, sdk/runanywhere-flutter/packages/runanywhere/android/src/main/kotlin/ai/runanywhere/sdk/RunAnywherePlugin.kt
Added Genie framework FFI mappings; extended RacModelInfoCStruct with supportsLora field; added getSocModel() helper (returns Build.SOC_MODEL on API S+ or Build.HARDWARE).
Flutter SDK: RAG & Public APIs
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_rag.dart, sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart, sdk/runanywhere-flutter/packages/runanywhere/lib/public/types/rag_types.dart, sdk/runanywhere-flutter/packages/runanywhere/lib/public/extensions/runanywhere_device.dart, sdk/runanywhere-flutter/packages/runanywhere/lib/runanywhere.dart
Refactored RAG API: moved from core RunAnywhere statics to extension methods; updated bridge types; added device extension for NPU chip detection; re-exported NPUChip types and new device APIs.
Flutter Genie Backend Package
sdk/runanywhere-flutter/packages/runanywhere_genie/*
Created new Flutter package with Kotlin (Android) and Swift (iOS) plugins, FFI bindings for native registration, Genie module class implementing RunAnywhereModule interface, error types, and pubspec configuration; handles dynamic native library loading and per-platform availability checks.
Kotlin SDK: Types & Device Detection
sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/core/types/ComponentTypes.kt, sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/core/types/NPUChip.kt, sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/foundation/device/DeviceInfoService.kt, sdk/runanywhere-kotlin/src/androidMain/kotlin/com/runanywhere/sdk/foundation/device/DeviceInfoService.kt, sdk/runanywhere-kotlin/src/androidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+Device.kt
Added InferenceFramework.GENIE enum; introduced NPUChip enum with SoC model matching; updated Android device info to prioritize Build.SOC_MODEL (API S+) over Build.HARDWARE; added device extension getChip() with multi-source fallback strategy (SOC_MODEL → HARDWARE → /proc/cpuinfo).
Kotlin SDK: Storage & Logging
sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/foundation/SDKLogger.kt, sdk/runanywhere-kotlin/src/androidMain/kotlin/com/runanywhere/sdk/security/SecureStorage.kt
Added genie logger instance; made addDestinationSync @Synchronized; fixed concurrent destination iteration.
Kotlin SDK: Model Management
sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/public/extensions/Models/ModelTypes.kt, sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/public/extensions/LLM/LLMTypes.kt, sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/extensions/CppBridgeModelRegistry.kt, sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+ModelManagement.jvmAndroid.kt, sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+Storage.jvmAndroid.kt
Added ModelFormat.QNN_CONTEXT; included GENIE in LLM framework relevance checks; updated model format/framework C++ enum mappings; enhanced archive extraction with pre-post file snapshotting and flattening logic; added downloaded model path persistence; improved model deletion with multi-format extension detection.
Kotlin SDK: Service Initialization
sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/public/RunAnywhere.kt, sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/PlatformBridge.kt, sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/CppBridge.kt, sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/extensions/CppBridgeEvents.kt
Changed Phase 2 services initialization to use coroutine Mutex instead of JVM monitor; made initializePlatformBridgeServices and initializeCppBridgeServices suspending; removed runBlocking wrapper; added Framework.GENIE = 10 constant; updated isNativeInitialized() to double-check both flags.
Kotlin SDK: LoRA
sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+LoRA.jvmAndroid.kt
Added post-download GGUF magic validation; improved error messages with adapter path; updated failure messaging to label returned value as error code.
React Native Core: Types & Device
sdk/runanywhere-react-native/packages/core/src/types/NPUChip.ts, sdk/runanywhere-react-native/packages/core/src/types/enums.ts, sdk/runanywhere-react-native/packages/core/src/Public/Extensions/RunAnywhere+Device.ts, sdk/runanywhere-react-native/packages/core/src/Public/Extensions/index.ts, sdk/runanywhere-react-native/packages/core/src/Public/RunAnywhere.ts
Added NPUChip interface and utility functions (chip lookup, URL construction); added LLMFramework.Genie enum; implemented getChip() with platform-gated Android-only detection via SoC model lookup and NPU chip mapping; updated RunAnywhere public API to expose getChip.
React Native Core: File System & Download
sdk/runanywhere-react-native/packages/core/src/services/FileSystem.ts, sdk/runanywhere-react-native/packages/core/src/Public/Extensions/RunAnywhere+Models.ts
Added download job tracking and cancelDownload() implementation; updated model format inference to support QNN_CONTEXT; improved model deletion with multi-format extension detection; simplified format/enum imports; enhanced error handling and empty-result detection.
React Native Core: VLM
sdk/runanywhere-react-native/packages/core/src/Public/Extensions/RunAnywhere+VLM.ts
Changed VLM module typing from strongly-typed alias to any for flexibility.
React Native Core C++
sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.hpp, sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.cpp, sdk/runanywhere-react-native/packages/core/android/CMakeLists.txt, sdk/runanywhere-react-native/packages/core/android/src/main/java/com/margelo/nitro/runanywhere/PlatformAdapterBridge.kt
Added error-state Mutex for thread safety; added Genie framework name mapping; made error getter/setter thread-safe; fixed WAV file parsing (use memcpy instead of reinterpret_cast); updated JSON serialization for transcription results; fixed freed-pointer access in generate latency tracking; updated Android SoC model detection strategy.
React Native Backend CMake
sdk/runanywhere-react-native/packages/core/android/CMakeLists.txt, sdk/runanywhere-react-native/packages/llamacpp/android/CMakeLists.txt, sdk/runanywhere-react-native/packages/onnx/android/CMakeLists.txt
Updated RAC header include paths to src/main/jniLibs/include; added backend include directories (rac/features/vlm, rac/backends); excluded RAGBridge.cpp and CompatibilityBridge.cpp from compiled sources.
React Native Backend Bridges
sdk/runanywhere-react-native/packages/core/cpp/bridges/CompatibilityBridge.hpp, sdk/runanywhere-react-native/packages/core/cpp/bridges/RAGBridge.hpp
Replaced implementations with inline stubs due to missing native symbols; changed default compatibility result fields from false to true; RAG methods return safe defaults; updated header includes and type fallbacks.
React Native LLama Backend
sdk/runanywhere-react-native/packages/llamacpp/cpp/HybridRunAnywhereLlama.cpp
Added error handling to numeric JSON parsing helpers with try/catch returning default values on conversion failure.
React Native Core Type Exports
sdk/runanywhere-react-native/packages/core/src/index.ts, sdk/runanywhere-react-native/packages/core/src/types/index.ts
Added exports for getChip function and NPUChip types plus utility functions; made NPU detection APIs publicly available.
Android Example App: Config & Manifest
examples/android/RunAnywhereAI/app/build.gradle.kts, examples/android/RunAnywhereAI/app/src/main/AndroidManifest.xml, examples/android/RunAnywhereAI/settings.gradle.kts
Added Genie Android dependency; enabled native library extraction; added libcdsprpc.so native library declaration; updated settings comments.
Android Example App: Data & Models
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/data/LoraExamplePrompts.kt, examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/data/ModelList.kt
Replaced LoRA adapter test prompts; updated LLM model to Q8_0 variant; added Genie NPU model support with chipset-aware model generation and conditional backend registration.
Android Example App: UI & Logic
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/ChatScreen.kt, examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/ChatViewModel.kt, examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelSelectionBottomSheet.kt
Updated starter prompt examples; added Genie model preference in model selection logic; added NPU framework UI indicator with color mapping and error dialog handling on model load failure.
Flutter Example App: Config & Plugins
examples/flutter/RunAnywhereAI/android/app/build.gradle, examples/flutter/RunAnywhereAI/android/app/src/main/AndroidManifest.xml, examples/flutter/RunAnywhereAI/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java, examples/flutter/RunAnywhereAI/ios/Runner/GeneratedPluginRegistrant.m, examples/flutter/RunAnywhereAI/pubspec.yaml
Added native library packaging options; enabled native library extraction; added libcdsprpc.so declaration; registered Genie plugin (replacing Onnx on Android, alongside Llama on iOS); added camera/file picker/image picker plugins; added Genie dependency.
Flutter Example App: Models & Framework
examples/flutter/RunAnywhereAI/lib/features/models/model_types.dart, examples/flutter/RunAnywhereAI/lib/features/models/model_list_view_model.dart, examples/flutter/RunAnywhereAI/lib/features/chat/chat_interface_view.dart
Added LLMFramework.genie enum with display name "Genie NPU"; added bidirectional framework conversion mapping; updated framework name fallback from llamaCpp to unknown.
Flutter Example App: Initialization
examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart
Added Genie NPU module registration with availability check, chip detection, and conditional model registration based on supported chip compatibility.
React Native Example App: Config
examples/react-native/RunAnywhereAI/android/app/build.gradle, examples/react-native/RunAnywhereAI/android/app/src/main/AndroidManifest.xml, examples/react-native/RunAnywhereAI/android/app/src/main/java/com/runanywhereaI/MainApplication.kt, examples/react-native/RunAnywhereAI/android/settings.gradle, examples/react-native/RunAnywhereAI/metro.config.js, examples/react-native/RunAnywhereAI/package.json
Added native library packaging options for Genie; enabled native library extraction; declared libcdsprpc.so; replaced RAG package with Genie package; updated settings and Metro config for Genie module resolution; changed document picker dependency; updated clean script with --ignore-scripts and patch-package.
React Native Example App: Models & Chat
examples/react-native/RunAnywhereAI/src/components/model/ModelSelectionSheet.tsx, examples/react-native/RunAnywhereAI/src/screens/ChatScreen.tsx, examples/react-native/RunAnywhereAI/src/screens/RAGScreen.tsx, examples/react-native/RunAnywhereAI/src/types/model.ts
Added Genie framework UI display (color, icon); removed debug console logs; updated model info to use SDK framework/preferred framework instead of hardcoding; switched document picker implementation and MIME type handling; added LLMFramework.Genie enum.
React Native Example App: Initialization
examples/react-native/RunAnywhereAI/App.tsx
Added Android-gated Genie backend bootstrap; implemented chipset-aware NPU model registration with download URL construction; conditional success logging based on chip detection.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

kotlin-sdk, flutter-sdk, react-native-sdk, feature, npu-support

Suggested reviewers

  • Siddhesh2377

Poem

🐰 Genie magic comes to life today,
With NPU chips now pointing the way,
From Flutter to Kotlin, React's new friend,
Qualcomm's speed helps models transcend! ✨

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch smonga/genie_support_v2

The runanywhere-genie repo is private (Qualcomm proprietary binaries),
so Flutter binary downloads must come from the public runanywhere-sdks
repo where the Genie ZIP is published as a GitHub release asset.

Made-with: Cursor
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/extensions/CppBridgeModelRegistry.kt (1)

90-100: ⚠️ Potential issue | 🟠 Major

Add missing Framework constants to align with C++ enum in rac_model_types.h.

The Kotlin registry is missing three framework constants that are defined and actively used in the C++ backend:

  • MLX = 7 (Apple Silicon VLM)
  • COREML = 8 (Core ML / Apple Neural Engine)
  • WHISPERKIT_COREML = 9 (WhisperKit CoreML STT)

These must be added between NONE = 6 and GENIE = 10 to maintain exact alignment with the C++ enum and ensure proper model framework resolution across platform boundaries.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/extensions/CppBridgeModelRegistry.kt`
around lines 90 - 100, The Framework object in CppBridgeModelRegistry is missing
three constants required to match the C++ enum in rac_model_types.h; add MLX =
7, COREML = 8, and WHISPERKIT_COREML = 9 inside the Framework object (between
NONE = 6 and GENIE = 10) so the Kotlin constants align exactly with the C++ enum
and model framework resolution across platforms.
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_model_registry.dart (1)

342-361: ⚠️ Potential issue | 🟠 Major

Missing genie case in reverse FFI conversion.

The _frameworkFromFfi function doesn't handle case 10 for Genie, but _frameworkToFfi does map genie to 10. This asymmetry means models retrieved from C++ with framework=10 will be incorrectly converted to InferenceFramework.unknown.

🐛 Proposed fix: Add genie case to _frameworkFromFfi
   static public_types.InferenceFramework _frameworkFromFfi(int framework) {
     switch (framework) {
       case 0:
         return public_types.InferenceFramework.onnx;
       case 1:
         return public_types.InferenceFramework.llamaCpp;
       case 2:
         return public_types.InferenceFramework.foundationModels;
       case 3:
         return public_types.InferenceFramework.systemTTS;
       case 4:
         return public_types.InferenceFramework.fluidAudio;
       case 5:
         return public_types.InferenceFramework.builtIn;
       case 6:
         return public_types.InferenceFramework.none;
+      case 10:
+        return public_types.InferenceFramework.genie;
       default:
         return public_types.InferenceFramework.unknown;
     }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_model_registry.dart`
around lines 342 - 361, The reverse FFI mapper _frameworkFromFfi is missing the
Genie mapping (case 10) that _frameworkToFfi emits; update _frameworkFromFfi to
handle integer 10 and return public_types.InferenceFramework.genie so values
produced by _frameworkToFfi round-trip correctly (keep the existing default to
unknown for other values).
sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.cpp (1)

2048-2055: ⚠️ Potential issue | 🟡 Minor

Preserve result.confidence in transcribeFile().

This path now always returns 0, even though the STT result already carries a confidence score and transcribe() exposes it. That regresses any caller that ranks or filters file transcriptions by confidence.

Suggested fix
             std::string transcribedText;
             if (result.text) {
                 transcribedText = std::string(result.text);
             }
+            float confidence = result.confidence;
 
             rac_stt_result_free(&result);
             LOGI("Transcription result: %s", transcribedText.c_str());
-            return "{\"text\":" + jsonString(transcribedText) + ",\"confidence\":0}";
+            return "{\"text\":" + jsonString(transcribedText) + ",\"confidence\":" + std::to_string(confidence) + "}";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.cpp`
around lines 2048 - 2055, The transcribeFile() return value currently hardcodes
confidence to 0; capture and preserve result.confidence before freeing the C
struct and include it in the JSON response instead of 0. Specifically, read
result.confidence into a local variable (e.g., double confidence =
result.confidence) prior to calling rac_stt_result_free(&result), then build the
return string using jsonString for text and the numeric confidence variable for
the "confidence" field so callers get the real score (references:
transcribeFile(), result, rac_stt_result_free, jsonString).
sdk/runanywhere-commons/src/backends/llamacpp/llamacpp_backend.cpp (1)

1263-1315: ⚠️ Potential issue | 🟠 Major

The zero-match validation returns without cleaning up the created LoRA adapter.

After llama_adapter_lora_init() succeeds, the check for matched_tensors == 0 (around line 1285) returns false before the adapter is added to the lora_adapters_ vector. This orphans the adapter pointer. The subsequent failures in recreate_context() and apply_lora_adapters() are properly handled with lora_adapters_.pop_back(), but only because the adapter was already stored in the vector—the zero-match path lacks this protection.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-commons/src/backends/llamacpp/llamacpp_backend.cpp` around
lines 1263 - 1315, The code leaks the adapter when llama_adapter_lora_init(...)
succeeds but matched_tensors == 0; before returning false you must free the
created adapter (call the corresponding cleanup, e.g.
llama_adapter_lora_free(adapter)) or push a LoraAdapterEntry into lora_adapters_
and reuse the existing pop_back cleanup logic; update the zero-match branch in
the load path (around llama_adapter_lora_init, matched_tensors check) to free
the adapter or store it so recreate_context()/apply_lora_adapters() cleanup
paths cover it, referencing llama_adapter_lora_init, llama_adapter_lora_free (or
the appropriate free function), LoraAdapterEntry, lora_adapters_,
recreate_context, and apply_lora_adapters.
sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+ModelManagement.jvmAndroid.kt (1)

1186-1191: ⚠️ Potential issue | 🟠 Major

Also evict the in-memory caches on delete.

availableModels() overlays registeredModels back on top of the C++ registry. Since deleteModel() only removes the bridge/storage entries, a programmatically registered model can reappear immediately after deletion until process restart.

Suggested fix
 actual suspend fun RunAnywhere.deleteModel(modelId: String) {
     if (!isInitialized) {
         throw SDKError.notInitialized("SDK not initialized")
     }
     CppBridgeStorage.delete(CppBridgeStorage.StorageNamespace.DOWNLOADS, modelId)
     CppBridgeModelRegistry.remove(modelId)
+    synchronized(modelCacheLock) {
+        registeredModels.removeAll { it.id == modelId }
+    }
+    synchronized(multiFileCacheLock) {
+        multiFileModelCache.remove(modelId)
+    }
+    synchronized(companionFilesLock) {
+        modelCompanionFiles.remove(modelId)
+    }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere`+ModelManagement.jvmAndroid.kt
around lines 1186 - 1191, DeleteModel currently removes C++ storage and registry
entries but does not evict in-memory caches, so a programmatically registered
model can reappear; update RunAnywhere.deleteModel to also remove modelId from
the in-memory registration and invalidate any cached available-models list used
by availableModels() (e.g., remove from registeredModels and clear/invalidate
the availableModels cache or modelsCache) after calling CppBridgeStorage.delete
and CppBridgeModelRegistry.remove so the model cannot immediately reappear.
🟠 Major comments (21)
sdk/runanywhere-react-native/packages/core/src/services/FileSystem.ts-509-517 (1)

509-517: ⚠️ Potential issue | 🟠 Major

Normalize download keys; current cancellation lookup can miss active jobs.

activeDownloadJobs is keyed by the modelId passed to downloadModel (Line 510), but cancellation callers may use a canonical ID without extension. That mismatch makes Line 886 miss the job and stopDownload is never called.

💡 Proposed fix
@@
-    activeDownloadJobs.set(modelId, downloadResult.jobId);
+    const downloadKey = getBaseModelId(modelId);
+    activeDownloadJobs.set(downloadKey, downloadResult.jobId);
@@
-      activeDownloadJobs.delete(modelId);
+      activeDownloadJobs.delete(downloadKey);
@@
   cancelDownload(modelId: string): boolean {
-    const jobId = activeDownloadJobs.get(modelId);
+    const downloadKey = getBaseModelId(modelId);
+    const jobId = activeDownloadJobs.get(downloadKey);
     if (jobId != null && RNFS) {
       RNFS.stopDownload(jobId);
-      activeDownloadJobs.delete(modelId);
+      activeDownloadJobs.delete(downloadKey);
       logger.info(`Cancelled download for: ${modelId} (jobId=${jobId})`);
       return true;
     }

Also applies to: 885-889

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-react-native/packages/core/src/services/FileSystem.ts` around
lines 509 - 517, The activeDownloadJobs map uses the raw modelId passed to
downloadModel which can differ from the canonical ID used by cancellation logic;
update downloadModel (where activeDownloadJobs.set and delete are used) to
normalize the key (e.g., derive the canonical ID the same way
stopDownload/cancellation lookup does—strip extensions or call the shared
canonicalization helper) before inserting and deleting so the cancellation
lookup (stopDownload) can find and cancel the job; ensure the same normalization
is applied to both the set, the finally delete, and any other locations that
access activeDownloadJobs.
sdk/runanywhere-react-native/packages/core/src/Public/Extensions/RunAnywhere+Models.ts-514-518 (1)

514-518: ⚠️ Potential issue | 🟠 Major

Don’t return success when native cancellation fails.

Line 515 returns a boolean, but it’s ignored. Current flow logs “Cancelled” and returns true even if no RNFS job was actually stopped.

💡 Proposed fix
 export async function cancelDownload(modelId: string): Promise<boolean> {
   if (activeDownloads.has(modelId)) {
     // Stop the native RNFS download job
-    FileSystem.cancelDownload(modelId);
+    const cancelled = FileSystem.cancelDownload(modelId);
+    if (!cancelled) {
+      logger.warning(`Failed to cancel native download: ${modelId}`);
+      return false;
+    }
     activeDownloads.delete(modelId);
     logger.info(`Cancelled download: ${modelId}`);
     return true;
   }
   return false;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-react-native/packages/core/src/Public/Extensions/RunAnywhere`+Models.ts
around lines 514 - 518, FileSystem.cancelDownload(modelId) is currently called
and its boolean result ignored, causing logger.info(`Cancelled download:
${modelId}`) and activeDownloads.delete(modelId) to run even when cancellation
failed; change the flow to capture the return value (or awaited result) from
FileSystem.cancelDownload(modelId), only call activeDownloads.delete(modelId)
and logger.info(...) when that result is truthy, and otherwise log an error (or
warning) and return false so the function does not report success when
cancellation failed; update the return value to reflect the actual cancel
result.
sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/foundation/SDKLogger.kt-335-336 (1)

335-336: ⚠️ Potential issue | 🟠 Major

Use one synchronization strategy for _destinations; current change is still race-prone.

Line 335 snapshots _destinations, but Line 361 introduces @Synchronized while other mutations use Mutex (and removeDestinationSync is still unlocked). Because reads/writes are not coordinated under a single lock, concurrent mutation risks remain (duplicate registration, inconsistent iteration/flush behavior).

Also applies to: 361-365

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/foundation/SDKLogger.kt`
around lines 335 - 336, The code currently mixes synchronization strategies for
_destinations (snapshot via _destinations.toList(), `@Synchronized` on some
methods, and a Mutex used elsewhere) which leaves race windows (e.g., duplicate
registration, inconsistent iteration/flush and an unlocked
removeDestinationSync). Pick a single synchronization strategy and apply it
everywhere: either guard all accesses/mutations of _destinations (reads, writes,
removeDestinationSync, iteration in the flush/write loop) with the same Mutex
(use withLock around reads and writes and removeDestinationSync) or consistently
use Kotlin `@Synchronized` on the same monitor for all methods and remove the
Mutex; ensure the iteration does not rely on a toList() snapshot without the
lock. Update methods such as removeDestinationSync, the loop that iterates
_destinations, and any register/remove functions to use the chosen lock so all
concurrent access is serialized.
sdk/runanywhere-flutter/packages/runanywhere_llamacpp/android/binary_config.gradle-14-14 (1)

14-14: ⚠️ Potential issue | 🟠 Major

Hardcoded local mode here has the same release-risk as ONNX config.

Line 14 forces local JNI usage and bypasses download fallback. Please make it property-driven for reproducible CI/release behavior.

🔧 Suggested fix
-    testLocal = true
+    testLocal = (project.findProperty("runanywhere.testLocal")?.toString()?.toBoolean() ?: false)

Based on learnings: "Applies to */gradle : Kotlin SDK: Use ./gradlew :runanywhere-kotlin:compileDebugKotlinAndroid -Prunanywhere.testLocal=false to build the Android target without requiring Android NDK (uses pre-built JNI libs from GitHub releases)."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere_llamacpp/android/binary_config.gradle`
at line 14, The file currently hardcodes testLocal = true which forces local JNI
usage; change it to read a Gradle project property (e.g.
project.findProperty("runanywhere.testLocal") or project.hasProperty) and parse
it as a boolean so builds can override with -Prunanywhere.testLocal=false;
update the assignment of testLocal in binary_config.gradle (the testLocal
symbol) to use the property with a sensible default (true) so CI/release can opt
out without editing the file.
sdk/runanywhere-flutter/packages/runanywhere_onnx/android/binary_config.gradle-14-14 (1)

14-14: ⚠️ Potential issue | 🟠 Major

Defaulting testLocal to true can break production/native-lib resolution flows.

Line 14 forces local-only binaries and disables release download fallback. Please make this property-driven so CI/release builds can reliably use remote artifacts.

🔧 Suggested fix
-    testLocal = true
+    testLocal = (project.findProperty("runanywhere.testLocal")?.toString()?.toBoolean() ?: false)

Based on learnings: "Applies to **/gradle.properties : The testLocal flag is set to true in gradle.properties; pass -Prunanywhere.testLocal=false to Gradle to avoid needing Android NDK."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere_onnx/android/binary_config.gradle`
at line 14, The hardcoded default testLocal = true in binary_config.gradle
forces local-only binaries; change it to read a Gradle project property with a
safe default (e.g., use project.findProperty('runanywhere.testLocal') ?: 'false'
or check project.hasProperty and convert to boolean) so CI/release can pass
-Prunanywhere.testLocal=false; update the testLocal assignment in
binary_config.gradle (the testLocal symbol) to parse the property value into a
boolean instead of always setting true.
sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/extensions/CppBridgeEvents.kt-807-807 (1)

807-807: ⚠️ Potential issue | 🟠 Major

Cross-platform InferenceFramework inconsistency detected: Swift does not support GENIE while Kotlin and C++ do.

The value GENIE = 10 in Kotlin correctly maps to C++ usage (both use hardcoded value 10 in conversion functions). However, Swift's InferenceFramework enum (lines 75–89 in ModelTypes.swift) does not include a GENIE case at all, only 10 cases: onnx, llamaCpp, foundationModels, systemTTS, fluidAudio, coreml, mlx, whisperKitCoreML, builtIn, none, unknown.

This creates a platform gap: if analytics or model data includes GENIE framework attribution from Android/C++, iOS will not be able to map it and may default to unknown or fail silently. Add GENIE to Swift's InferenceFramework enum with appropriate raw value to maintain parity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/extensions/CppBridgeEvents.kt`
at line 807, Add a GENIE case to the Swift InferenceFramework enum in
ModelTypes.swift so it matches Kotlin/C++ (use raw value 10 to mirror const val
GENIE = 10); update any switches or decoding/mapping code that converts numeric
framework values to InferenceFramework to handle the new .genie case so iOS will
map GENIE-attributed analytics/model data correctly.
sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/CppBridge.kt-562-566 (1)

562-566: ⚠️ Potential issue | 🟠 Major

Do not report initialized when native readiness check throws.

At Line 564 through Line 566, a JNI failure can still return true via _isInitialized, which can cause false-ready state. Return false on exception.

Suggested fix
         return try {
             RunAnywhereBridge.racIsInitialized()
         } catch (_: Exception) {
-            _isInitialized
+            false
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/CppBridge.kt`
around lines 562 - 566, The current catch block returns the fallback
_isInitialized when RunAnywhereBridge.racIsInitialized() throws, which can
report a false-ready state; change the catch in the method that calls
RunAnywhereBridge.racIsInitialized() so that any Exception results in returning
false (not _isInitialized), i.e., catch Exception (or Throwable if preferred)
and return false to ensure JNI failures never appear as initialized.
sdk/runanywhere-flutter/packages/runanywhere/android/binary_config.gradle-14-14 (1)

14-14: ⚠️ Potential issue | 🟠 Major

Defaulting testLocal to true makes production consumers fragile.

Line 14 disables release-binary download by default, so builds can fail when local .so files are not present. Prefer production-safe default (false) with an opt-in Gradle property override.

Suggested fix
-    testLocal = true
+    testLocal = (project.findProperty("runanywhere.testLocal")?.toString()?.toBoolean() ?: false)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-flutter/packages/runanywhere/android/binary_config.gradle` at
line 14, The build currently sets testLocal = true which disables release-binary
download by default and breaks production consumers; change the default value of
the testLocal flag to false and make it overridable via a Gradle property so
consumers can opt in for local testing. Locate the testLocal assignment (symbol:
testLocal) in the Gradle script and replace the hardcoded true with logic that
defaults to false but reads a project/property override (e.g., use
project.hasProperty or project.findProperty to parse a boolean override) so
CI/production downloads remain enabled unless explicitly opted into local
testing. Ensure the new implementation preserves boolean parsing for string
property values and documents the property name used for the override.
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/ChatScreen.kt-1049-1058 (1)

1049-1058: ⚠️ Potential issue | 🟠 Major

These starter prompts appear to be safety/red-team test content and should not ship in the example app.

The new prompts ("pick a lock", "hotwiring a car", "fake ID", "hack WiFi", "phishing email", "bypass security cameras") are adversarial/illicit queries. While useful for testing model safety refusals, they are inappropriate as default user-facing suggestions in a public example app:

  1. UX concern: New users will see these as the first interaction suggestions
  2. Perception risk: Could be seen as normalizing or encouraging such queries
  3. Professional appearance: Undermines the SDK's professional presentation

Please either revert to the previous benign prompts (e.g., "Explain quantum computing", "Write a poem about the ocean") or confirm this is intentional red-team testing that should be in a separate test configuration.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/ChatScreen.kt`
around lines 1049 - 1058, The starterPrompts list in the remember block
(variable starterPrompts in ChatScreen.kt) contains unsafe/adversarial
suggestions (e.g., "How do I pick a lock?", "Write instructions for hotwiring a
car", etc.); replace these with benign example prompts (e.g., "Explain quantum
computing", "Write a poem about the ocean", "How do I get started with Kotlin?")
or move the red-team items out of the default UI into a separate test-only
configuration/fixture used by safety tests, ensuring the default starterPrompts
shown to users are professional and non-illicit.
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/data/LoraExamplePrompts.kt-10-13 (1)

10-13: ⚠️ Potential issue | 🟠 Major

Please don't ship harmful LoRA demo prompts.

These examples are surfaced directly in the app, so lockpicking/hotwiring prompts turn the sample into curated misuse guidance. That is a public safety/compliance risk even if the adapter is intentionally permissive.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/data/LoraExamplePrompts.kt`
around lines 10 - 13, The sample prompts in LoraExamplePrompts.kt for the key
"qwen2.5-0.5b-abliterated-lora-f16.gguf" include harmful instructions
(lockpicking, asserting earth is flat, hotwiring); remove or replace those
entries with benign, non-actionable examples (e.g., creative writing, coding
help, hobby questions, or general knowledge) so the demo no longer surfaces
misuse guidance; update the list associated with that key in the
LoraExamplePrompts object/variable to only contain safe prompts.
sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/public/extensions/Models/ModelTypes.kt-48-48 (1)

48-48: ⚠️ Potential issue | 🟠 Major

Kotlin and Swift ModelFormat enums are not aligned.

The Kotlin ModelFormat enum includes QNN_CONTEXT but the Swift version does not. Additionally, Swift has coreml which is missing from Kotlin. The file header claims to "mirror Swift ModelFormat exactly," but verification shows they are out of sync. Per the coding guidelines, iOS is the source of truth — align the Kotlin enum with Swift, or update the header comment to document the intentional divergence.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/public/extensions/Models/ModelTypes.kt`
at line 48, The ModelFormat enum in Kotlin (symbol: ModelFormat) is out of sync
with the Swift definition: it contains QNN_CONTEXT but is missing coreml, and
the file header claiming an exact mirror is therefore incorrect. Update the
Kotlin enum to match the Swift iOS source of truth by removing QNN_CONTEXT (or
otherwise matching Swift's exact members) and adding coreml if present in Swift,
and/or adjust the file header comment to state the intentional divergence;
specifically modify the ModelFormat enum entries to exactly mirror Swift's
members (add/remove the symbols such as coreml and QNN_CONTEXT to match) and
update the header comment accordingly.
sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart-415-430 (1)

415-430: ⚠️ Potential issue | 🟠 Major

Don't delete the extracted root after a directory move fails.

When item is a Directory and rename(target) throws, the catch only logs the failure. The subsequent extractedDir.delete(recursive: true) then deletes that still-unmoved subtree, so nested model assets can be lost silently. Abort flattening or recursively copy directories before removing the source directory.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart`
around lines 415 - 430, The code currently iterates innerItems from extractedDir
and attempts FileSystemEntity.rename for each item, but when a Directory rename
fails the catch only logs and later extractedDir.delete(recursive: true) can
remove still-needed subtrees; update the logic in the block around
extractedDir.list()/for (final item in innerItems) so that if any directory
failed to move you either (a) perform a recursive copy of that Directory to the
target (copy contents then delete source) before proceeding, or (b) abort the
flattening operation and skip deleting extractedDir; specifically, modify the
try/catch for (item as FileSystemEntity).rename(target) to detect Directory
instances (item is Directory) and call a recursive copy helper (or set a boolean
failedMove flag) so you do not call extractedDir.delete(recursive: true) when
directories remain unmoved (refer to extractedDir, innerItems, rename,
item.copy, item.delete, and the extractedDir.delete call).
sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart-335-376 (1)

335-376: ⚠️ Potential issue | 🟠 Major

This extractor is now Android-specific.

Process.run('tar', ...) / Process.run('unzip', ...) assumes those binaries are available in the app runtime. That is fine for Android/toybox, but this shared Flutter downloader also runs on non-Android targets where spawning tar/unzip is not available, so archive extraction will fail there. Keep the shell fast path platform-gated and preserve a native/Dart fallback elsewhere.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart`
around lines 335 - 376, The extractor currently always calls external binaries
via Process.run in _extractArchive; gate that fast path to Android only by
checking Platform.isAndroid (import dart:io) before invoking 'tar'/'unzip', and
for non-Android platforms call the existing Dart-native extraction fallback
(e.g., your pure-Dart archive extraction routine or a new helper) to preserve
cross-platform behavior; ensure the .zip branch similarly uses the platform
check and that errors/return values remain consistent with the current contract
of _extractArchive.
examples/react-native/RunAnywhereAI/App.tsx-248-286 (1)

248-286: ⚠️ Potential issue | 🟠 Major

Keep Genie setup best-effort instead of startup-fatal.

This block runs during initializeSDK(). If Genie.register(), getChip(), getNPUDownloadUrl(), or any RunAnywhere.registerModel() call throws, registerModulesAndModels() rejects and the whole example app lands on the initialization error screen even though Genie is optional. Catch and log failures inside this block, then continue with the existing LlamaCPP/ONNX registration path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/react-native/RunAnywhereAI/App.tsx` around lines 248 - 286, Wrap the
entire Genie NPU registration block inside
registerModulesAndModels()/initializeSDK() in a try/catch so failures are
non-fatal: call Genie.register(), getChip(), build genieModels, map to
RunAnywhere.registerModel(...) and await Promise.all(...) inside the try, and on
any thrown error catch it, console.error a descriptive message including the
caught error, then continue (do not rethrow) so the LlamaCPP/ONNX registration
path still runs; reference Genie.register, getChip, getNPUDownloadUrl,
RunAnywhere.registerModel, and LLMFramework.Genie when locating the code to
change.
sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.cpp-274-275 (1)

274-275: ⚠️ Potential issue | 🟠 Major

Use the RAC_FRAMEWORK_GENIE symbol instead of hard-coded 10.

These three sites hard-code Genie as 10. This couples the React Native bridge to a private numeric assumption instead of the RACommons ABI enum. If the enum is reordered or the library is updated, these sites will silently mismatch. Other frameworks in the same code paths correctly use the symbol (e.g., RAC_FRAMEWORK_COREML). Replace the literal with RAC_FRAMEWORK_GENIE to stay in sync with the shared C API.

Also applies to: lines 909–910, 983–984

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.cpp`
around lines 274 - 275, Replace the hard-coded numeric literal 10 with the enum
symbol RAC_FRAMEWORK_GENIE wherever the Genie framework is detected (e.g., in
the conditional that currently returns (rac_inference_framework_t)10 and the two
other occurrences noted near the same code paths). Update the return expression
to return RAC_FRAMEWORK_GENIE (cast only if necessary to
rac_inference_framework_t) so the React Native bridge uses the shared C API enum
rather than a private numeric constant, and ensure the translation unit has the
enum definition in scope (include the header that defines RAC_FRAMEWORK_GENIE if
not already present).
sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart-2547-2548 (1)

2547-2548: ⚠️ Potential issue | 🟠 Major

Keep compatibility shims for the moved RAG API.

RunAnywhere no longer exposes any rag* statics, so upgrading turns existing RunAnywhere.ragCreatePipeline(...) call sites into compile errors. If the extension is the long-term home, please leave thin delegates here or document this as an explicit breaking SDK change.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart`
around lines 2547 - 2548, RunAnywhere removed its rag* static methods causing
breaking changes; restore thin compatibility shims on the RunAnywhere class that
delegate to the new RunAnywhereRAG extension methods (e.g., implement
RunAnywhere.ragCreatePipeline(...), RunAnywhere.ragLoadPipeline(...), and any
other rag* statics that were removed) by forwarding arguments and returning the
extension call results so existing call sites continue to compile while the real
implementation lives in RunAnywhereRAG.
sdk/runanywhere-commons/src/infrastructure/model_management/model_types.cpp-153-160 (1)

153-160: ⚠️ Potential issue | 🟠 Major

QNN_CONTEXT is still undiscoverable from path-based inference.

rac_model_format_extension(RAC_MODEL_FORMAT_QNN_CONTEXT) now returns "bin", but the detection helpers below still resolve .bin to RAC_MODEL_FORMAT_BINRAC_FRAMEWORK_FLUID_AUDIO. Any lazy discovery/restore path that infers type from filenames will still misclassify Genie payloads after restart. Please preserve Genie’s framework/format out of band or add Genie-specific directory inspection instead of relying on the raw extension.

As per coding guidelines, "Model registry must support lazy initialization and provide path resolution for all model types (GGUF, ONNX, GGML)."

Also applies to: 177-178, 423-425

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@sdk/runanywhere-commons/src/infrastructure/model_management/model_types.cpp`
around lines 153 - 160, The Genie payloads are being misclassified because
rac_model_format_extension(RAC_MODEL_FORMAT_QNN_CONTEXT) returns "bin" but the
path-based detection logic still maps ".bin" to
RAC_MODEL_FORMAT_BIN/RAC_FRAMEWORK_FLUID_AUDIO; update the path-detection logic
(the detection helpers referenced in the comment and any path-to-format
resolver) so that when the model framework is or can be identified as
RAC_FRAMEWORK_GENIE it prefers RAC_MODEL_FORMAT_QNN_CONTEXT
(RAC_MODEL_FORMAT_QNN_CONTEXT) over the generic BIN mapping, or alternatively
preserve the Genie framework/format out-of-band in the model registry metadata
used for lazy initialization (so restore/discovery uses the stored
framework+format rather than inferring from extension); apply the same change to
the other occurrences noted (the blocks corresponding to the comment at 177-178
and 423-425).
sdk/runanywhere-flutter/packages/runanywhere_genie/android/binary_config.gradle-37-49 (1)

37-49: ⚠️ Potential issue | 🟠 Major

Validate the full local Genie runtime before skipping downloads.

checkLocalLibsExist() returns true as soon as the JNI shim exists. In local mode that lets the build bypass the release zip even if required runtime .so files are missing, which is exactly how SM8850/V81 support can regress when libQnnHtpV81.so wasn't copied into jniLibs.

Suggested fix
-        // Check for Genie backend library
-        def genieLib = new File(arm64Dir, 'librac_backend_genie_jni.so')
-        return genieLib.exists()
+        // Check for the JNI shim plus the runtime libraries required by this package.
+        def requiredLibs = [
+            'librac_backend_genie_jni.so',
+            'libQnnHtpV81.so',
+            // add the remaining Genie runtime .so files shipped in the release zip
+        ]
+        return requiredLibs.every { new File(arm64Dir, it).exists() }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere_genie/android/binary_config.gradle`
around lines 37 - 49, The current checkLocalLibsExist closure returns true as
soon as the JNI shim (librac_backend_genie_jni.so) is present, which allows
skipping downloads even when other required runtime .so files are missing;
update checkLocalLibsExist to verify the presence of the full set of required
native libraries in arm64-v8a (e.g., librac_backend_genie_jni.so,
libQnnHtpV81.so and any other runtime .so names your runtime needs) by
enumerating and confirming each file exists in jniLibsDir/arm64-v8a before
returning true; modify the logic around the genieLib variable and add checks for
the additional filenames so the function only returns true when all required .so
files are present.
sdk/runanywhere-flutter/packages/runanywhere_genie/android/src/main/kotlin/ai/runanywhere/sdk/genie/GeniePlugin.kt-19-31 (1)

19-31: ⚠️ Potential issue | 🟠 Major

Don't report Genie metadata after a JNI load failure.

The UnsatisfiedLinkError path only logs, but getBackendName() / getBackendVersion() still return success unconditionally. That makes Flutter-side availability probes treat Genie as present even when rac_backend_genie_jni is missing, and the reported version is already stale vs the 0.3.0 binaries configured in binary_config.gradle.

Suggested fix
     companion object {
         private const val CHANNEL_NAME = "runanywhere_genie"
-        private const val BACKEND_VERSION = "0.1.6"
+        private const val BACKEND_VERSION = "0.3.0"
         private const val BACKEND_NAME = "Genie"
+        `@Volatile` private var nativeLoaded = false

         init {
             // Load Genie backend native libraries
             try {
                 System.loadLibrary("rac_backend_genie_jni")
+                nativeLoaded = true
             } catch (e: UnsatisfiedLinkError) {
                 // Library may not be available in all configurations
                 android.util.Log.w("Genie", "Failed to load rac_backend_genie_jni: ${e.message}")
             }
         }
@@
             "getBackendVersion" -> {
-                result.success(BACKEND_VERSION)
+                if (nativeLoaded) {
+                    result.success(BACKEND_VERSION)
+                } else {
+                    result.error("unavailable", "Genie JNI library not loaded", null)
+                }
             }
             "getBackendName" -> {
-                result.success(BACKEND_NAME)
+                if (nativeLoaded) {
+                    result.success(BACKEND_NAME)
+                } else {
+                    result.error("unavailable", "Genie JNI library not loaded", null)
+                }
             }

Also applies to: 45-49

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere_genie/android/src/main/kotlin/ai/runanywhere/sdk/genie/GeniePlugin.kt`
around lines 19 - 31, The companion object currently logs JNI load failure but
still reports backend metadata; add a private boolean (e.g., libraryLoaded) set
to true inside the try after System.loadLibrary("rac_backend_genie_jni") and
false in the catch, then make getBackendName() and getBackendVersion() check
that flag and return null/empty (or otherwise indicate unavailable) when the
library failed to load so Flutter availability probes don't treat Genie as
present; also update the BACKEND_VERSION constant from "0.1.6" to "0.3.0" to
match the configured native binaries (or derive it from native if available).
sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+ModelManagement.jvmAndroid.kt-875-920 (1)

875-920: ⚠️ Potential issue | 🟠 Major

Ignore archive metadata entries before classifying the extracted layout.

A zip with one real model folder plus __MACOSX/ or another metadata directory makes newDirs.size == 1 && newFiles.isEmpty() false here. That falls into the flat-archive branch and moves the whole payload folder under expectedModelDir, recreating the nesting bug this PR is trying to fix.

Suggested fix
-        val newDirs = newItems.filter { it.isDirectory }
-        val newFiles = newItems.filter { it.isFile }
+        val payloadItems =
+            newItems.filterNot { item ->
+                item.name == "__MACOSX" || item.name.startsWith(".")
+            }
+        val newDirs = payloadItems.filter { it.isDirectory }
+        val newFiles = payloadItems.filter { it.isFile }
@@
-                val itemsToMove = newItems.filter { it != expectedModelDir }
+                val itemsToMove = payloadItems
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere`+ModelManagement.jvmAndroid.kt
around lines 875 - 920, The extracted-layout detection incorrectly treats
metadata folders (e.g. "__MACOSX", ".DS_Store" or other archive metadata) as
real content, causing the flat-archive branch to run; update the logic around
newItems/newDirs/newFiles in the model extraction code (the block that computes
tempArchiveName, expectedModelDir, newItems and then newDirs/newFiles) to first
filter out known metadata entries (e.g. "__MACOSX", entries starting with "." or
a small METADATA_DIRS set) and then use those filtered lists for the conditional
that checks newDirs.size == 1 && newFiles.isEmpty() and for the itemsToMove
computation so metadata directories are ignored when deciding whether to rename
the single extracted root or treat the archive as flat.
sdk/runanywhere-flutter/packages/runanywhere/lib/public/extensions/runanywhere_device.dart-18-19 (1)

18-19: ⚠️ Potential issue | 🟠 Major

Fix: getChip() can't be called as RunAnywhere.getChip()

In Dart, static methods on extensions must be called via the extension name: RunAnywhereDevice.getChip(), not RunAnywhere.getChip(). This breaks the example in npu_chip.dart (line 8), which shows the wrong syntax. Additionally, the method only catches PlatformException—it won't catch MissingPluginException, which is a separate exception type thrown when the native platform channel has no handler. Either move the method directly onto RunAnywhere or update all examples to use RunAnywhereDevice.getChip() and add MissingPluginException to the exception handler.

Also applies to: 34-44

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/public/extensions/runanywhere_device.dart`
around lines 18 - 19, The static API in the RunAnywhereDevice extension (e.g.,
RunAnywhereDevice.getChip and the other static methods declared in that
extension) must be callable as RunAnywhere.getChip() in your examples and
robustly handle platform plugin absence; either move these static methods out of
the extension and into the RunAnywhere class so callers can use
RunAnywhere.getChip(), or if you keep them on the extension update all examples
to call RunAnywhereDevice.getChip(); also update the try/catch in getChip (and
the other static methods in the same extension) to catch both PlatformException
and MissingPluginException and include handling/logging for both.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 08ac6279-bbbb-4a77-bfdf-f460719b83b9

📥 Commits

Reviewing files that changed from the base of the PR and between 7be8565 and c093ec7.

⛔ Files ignored due to path filters (4)
  • docs/gifs/npu-model-tag-screenshot.png is excluded by !**/*.png
  • examples/android/RunAnywhereAI/app/src/main/jniLibs/arm64-v8a/libQnnHtpV81.so is excluded by !**/*.so
  • examples/react-native/RunAnywhereAI/package-lock.json is excluded by !**/package-lock.json
  • examples/react-native/RunAnywhereAI/yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (125)
  • .gitignore
  • .idea/vcs.xml
  • docs/impl/lora_adapter_support.md
  • docs/sdks/flutter-sdk.md
  • docs/sdks/kotlin-sdk.md
  • docs/sdks/react-native-sdk.md
  • examples/android/RunAnywhereAI/app/build.gradle.kts
  • examples/android/RunAnywhereAI/app/src/main/AndroidManifest.xml
  • examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/data/LoraExamplePrompts.kt
  • examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/data/ModelList.kt
  • examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/ChatScreen.kt
  • examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/ChatViewModel.kt
  • examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelSelectionBottomSheet.kt
  • examples/android/RunAnywhereAI/settings.gradle.kts
  • examples/flutter/RunAnywhereAI/android/app/build.gradle
  • examples/flutter/RunAnywhereAI/android/app/src/main/AndroidManifest.xml
  • examples/flutter/RunAnywhereAI/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
  • examples/flutter/RunAnywhereAI/ios/Runner/GeneratedPluginRegistrant.m
  • examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart
  • examples/flutter/RunAnywhereAI/lib/features/chat/chat_interface_view.dart
  • examples/flutter/RunAnywhereAI/lib/features/models/model_list_view_model.dart
  • examples/flutter/RunAnywhereAI/lib/features/models/model_types.dart
  • examples/flutter/RunAnywhereAI/pubspec.yaml
  • examples/react-native/RunAnywhereAI/App.tsx
  • examples/react-native/RunAnywhereAI/android/app/build.gradle
  • examples/react-native/RunAnywhereAI/android/app/src/main/AndroidManifest.xml
  • examples/react-native/RunAnywhereAI/android/app/src/main/java/com/runanywhereaI/MainApplication.kt
  • examples/react-native/RunAnywhereAI/android/settings.gradle
  • examples/react-native/RunAnywhereAI/metro.config.js
  • examples/react-native/RunAnywhereAI/package.json
  • examples/react-native/RunAnywhereAI/src/components/model/ModelSelectionSheet.tsx
  • examples/react-native/RunAnywhereAI/src/screens/ChatScreen.tsx
  • examples/react-native/RunAnywhereAI/src/screens/RAGScreen.tsx
  • examples/react-native/RunAnywhereAI/src/types/model.ts
  • lefthook.yml
  • sdk/runanywhere-commons/include/rac/infrastructure/model_management/rac_model_types.h
  • sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt
  • sdk/runanywhere-commons/src/backends/llamacpp/llamacpp_backend.cpp
  • sdk/runanywhere-commons/src/backends/llamacpp/rac_llm_llamacpp.cpp
  • sdk/runanywhere-commons/src/features/diffusion/diffusion_json.cpp
  • sdk/runanywhere-commons/src/features/llm/llm_component.cpp
  • sdk/runanywhere-commons/src/features/llm/rac_llm_service.cpp
  • sdk/runanywhere-commons/src/infrastructure/model_management/model_assignment.cpp
  • sdk/runanywhere-commons/src/infrastructure/model_management/model_paths.cpp
  • sdk/runanywhere-commons/src/infrastructure/model_management/model_registry.cpp
  • sdk/runanywhere-commons/src/infrastructure/model_management/model_types.cpp
  • sdk/runanywhere-commons/src/infrastructure/registry/service_registry.cpp
  • sdk/runanywhere-commons/src/infrastructure/telemetry/telemetry_manager.cpp
  • sdk/runanywhere-flutter/packages/runanywhere/CHANGELOG.md
  • sdk/runanywhere-flutter/packages/runanywhere/android/binary_config.gradle
  • sdk/runanywhere-flutter/packages/runanywhere/android/src/main/jniLibs/.gitkeep
  • sdk/runanywhere-flutter/packages/runanywhere/android/src/main/kotlin/ai/runanywhere/sdk/RunAnywherePlugin.kt
  • sdk/runanywhere-flutter/packages/runanywhere/ios/Frameworks/.gitkeep
  • sdk/runanywhere-flutter/packages/runanywhere/lib/core/types/model_types.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/core/types/npu_chip.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_model_paths.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_model_registry.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_rag.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/native/type_conversions/model_types_cpp_bridge.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/public/extensions/runanywhere_device.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/public/types/rag_types.dart
  • sdk/runanywhere-flutter/packages/runanywhere/lib/runanywhere.dart
  • sdk/runanywhere-flutter/packages/runanywhere/pubspec.yaml
  • sdk/runanywhere-flutter/packages/runanywhere_genie/CHANGELOG.md
  • sdk/runanywhere-flutter/packages/runanywhere_genie/LICENSE
  • sdk/runanywhere-flutter/packages/runanywhere_genie/android/binary_config.gradle
  • sdk/runanywhere-flutter/packages/runanywhere_genie/android/build.gradle
  • sdk/runanywhere-flutter/packages/runanywhere_genie/android/src/main/AndroidManifest.xml
  • sdk/runanywhere-flutter/packages/runanywhere_genie/android/src/main/jniLibs/.gitkeep
  • sdk/runanywhere-flutter/packages/runanywhere_genie/android/src/main/kotlin/ai/runanywhere/sdk/genie/GeniePlugin.kt
  • sdk/runanywhere-flutter/packages/runanywhere_genie/ios/Classes/GeniePlugin.swift
  • sdk/runanywhere-flutter/packages/runanywhere_genie/ios/runanywhere_genie.podspec
  • sdk/runanywhere-flutter/packages/runanywhere_genie/lib/genie.dart
  • sdk/runanywhere-flutter/packages/runanywhere_genie/lib/genie_error.dart
  • sdk/runanywhere-flutter/packages/runanywhere_genie/lib/native/genie_bindings.dart
  • sdk/runanywhere-flutter/packages/runanywhere_genie/lib/runanywhere_genie.dart
  • sdk/runanywhere-flutter/packages/runanywhere_genie/pubspec.yaml
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/android/binary_config.gradle
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/android/src/main/jniLibs/.gitkeep
  • sdk/runanywhere-flutter/packages/runanywhere_llamacpp/ios/Frameworks/.gitkeep
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/android/binary_config.gradle
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/android/src/main/jniLibs/.gitkeep
  • sdk/runanywhere-flutter/packages/runanywhere_onnx/ios/Frameworks/.gitkeep
  • sdk/runanywhere-kotlin/settings.gradle.kts
  • sdk/runanywhere-kotlin/src/androidMain/kotlin/com/runanywhere/sdk/foundation/device/DeviceInfoService.kt
  • sdk/runanywhere-kotlin/src/androidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+Device.kt
  • sdk/runanywhere-kotlin/src/androidMain/kotlin/com/runanywhere/sdk/security/SecureStorage.kt
  • sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/core/types/ComponentTypes.kt
  • sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/core/types/NPUChip.kt
  • sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/foundation/SDKLogger.kt
  • sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/foundation/device/DeviceInfoService.kt
  • sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/public/RunAnywhere.kt
  • sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/public/extensions/LLM/LLMTypes.kt
  • sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/public/extensions/Models/ModelTypes.kt
  • sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+Device.kt
  • sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/CppBridge.kt
  • sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/extensions/CppBridgeEvents.kt
  • sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/extensions/CppBridgeModelPaths.kt
  • sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/foundation/bridge/extensions/CppBridgeModelRegistry.kt
  • sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/PlatformBridge.kt
  • sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+LoRA.jvmAndroid.kt
  • sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+ModelManagement.jvmAndroid.kt
  • sdk/runanywhere-kotlin/src/jvmAndroidMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+Storage.jvmAndroid.kt
  • sdk/runanywhere-kotlin/src/jvmMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+Device.kt
  • sdk/runanywhere-react-native/packages/core/android/CMakeLists.txt
  • sdk/runanywhere-react-native/packages/core/android/src/main/java/com/margelo/nitro/runanywhere/PlatformAdapterBridge.kt
  • sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.cpp
  • sdk/runanywhere-react-native/packages/core/cpp/HybridRunAnywhereCore.hpp
  • sdk/runanywhere-react-native/packages/core/cpp/bridges/CompatibilityBridge.hpp
  • sdk/runanywhere-react-native/packages/core/cpp/bridges/RAGBridge.hpp
  • sdk/runanywhere-react-native/packages/core/src/Public/Extensions/RunAnywhere+Device.ts
  • sdk/runanywhere-react-native/packages/core/src/Public/Extensions/RunAnywhere+Models.ts
  • sdk/runanywhere-react-native/packages/core/src/Public/Extensions/RunAnywhere+VLM.ts
  • sdk/runanywhere-react-native/packages/core/src/Public/Extensions/index.ts
  • sdk/runanywhere-react-native/packages/core/src/Public/RunAnywhere.ts
  • sdk/runanywhere-react-native/packages/core/src/index.ts
  • sdk/runanywhere-react-native/packages/core/src/services/FileSystem.ts
  • sdk/runanywhere-react-native/packages/core/src/types/NPUChip.ts
  • sdk/runanywhere-react-native/packages/core/src/types/enums.ts
  • sdk/runanywhere-react-native/packages/core/src/types/index.ts
  • sdk/runanywhere-react-native/packages/llamacpp/android/CMakeLists.txt
  • sdk/runanywhere-react-native/packages/llamacpp/cpp/HybridRunAnywhereLlama.cpp
  • sdk/runanywhere-react-native/packages/onnx/android/CMakeLists.txt

Comment on lines 95 to +96
private val lock = Any()
private val servicesMutex = Mutex()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

reset() can race with phase-2 initialization now.

Phase 2 now uses servicesMutex, but phase 1 and reset() still only synchronize on lock. That leaves a window where one coroutine tears the bridge down while another is inside initializeCppBridgeServices(), after which _areServicesReady can be set back to true on a reset SDK. Use one coordination primitive for init/reset, or acquire servicesMutex during reset as well.

Also applies to: 246-269

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/public/RunAnywhere.kt`
around lines 95 - 96, The reset() path races with phase-2 initialization because
phase 1 and reset use the plain lock while initializeCppBridgeServices() and
other phase-2 code use servicesMutex; fix this by unifying synchronization: have
reset() acquire servicesMutex for the duration of teardown (instead of or in
addition to using lock) so it cannot run concurrently with
initializeCppBridgeServices(), and update any phase-1 init code (or remove the
plain lock) to use servicesMutex as the single coordination primitive; ensure
you guard reads/writes of _areServicesReady and any bridge setup/teardown inside
the servicesMutex-protected section.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@sdk/runanywhere-flutter/packages/runanywhere_genie/android/binary_config.gradle`:
- Around line 31-49: The current binary_config uses arm64-only paths/URL
(genieAndroidUrl, checkLocalLibsExist, arm64-v8a, genieLib) while build.gradle
declares arm64-v8a, armeabi-v7a and x86_64; update either side: modify
checkLocalLibsExist and shouldDownloadAndroidLibs to iterate a list of ABIs
(e.g., ['arm64-v8a','armeabi-v7a','x86_64']) and construct per-ABI URLs/paths
(instead of genieAndroidUrl fixed to arm64) and validate each ABI's .so (e.g.,
look for appropriate librac_backend_genie_jni.so under each jniLibsDir/<ABI>),
or alternatively tighten build.gradle's abiFilters to only include 'arm64-v8a'
to match the existing genieAndroidUrl and checks; ensure unique symbols to
change are genieAndroidUrl, shouldDownloadAndroidLibs, and checkLocalLibsExist
so ABI behavior is consistent across config and build.gradle.
- Around line 14-36: The build file hardcodes testLocal = true, preventing
Gradle property overrides; change testLocal to read the Gradle property (e.g.,
project.findProperty("runanywhere.testLocal") or project.hasProperty) and parse
it to a boolean so -Prunanywhere.testLocal=false flips behavior, and ensure
shouldDownloadAndroidLibs (the closure) uses that resolved boolean; update
references to testLocal and keep genieVersion, binariesBaseUrl, and
genieAndroidUrl unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c1920b05-885a-4e53-af07-07c701aa04aa

📥 Commits

Reviewing files that changed from the base of the PR and between c093ec7 and c867708.

📒 Files selected for processing (1)
  • sdk/runanywhere-flutter/packages/runanywhere_genie/android/binary_config.gradle

Comment on lines +14 to +36
testLocal = true

// =============================================================================
// Version Configuration (MUST match Swift Package.swift and Kotlin build.gradle.kts)
// =============================================================================
genieVersion = "0.3.0"

// =============================================================================
// Remote binary URLs
// RABackendGenie hosted on the public runanywhere-sdks repo
// (runanywhere-genie is private, so binaries are published here)
// =============================================================================
binariesGitHubOrg = "RunanywhereAI"
binariesRepo = "runanywhere-sdks"
binariesBaseUrl = "https://github.com/${binariesGitHubOrg}/${binariesRepo}/releases/download"

// Android native libraries package
genieAndroidUrl = "${binariesBaseUrl}/genie-v${genieVersion}/RABackendGENIE-android-arm64-v8a-v${genieVersion}.zip"

// Helper method to check if we should download
shouldDownloadAndroidLibs = { ->
return !testLocal
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Make testLocal configurable from Gradle properties.

Line 14 hardcodes testLocal = true, so -Prunanywhere.testLocal=false won’t switch this package to download mode. That can break CI/release flows expecting remote prebuilt JNI libs.

Suggested fix
-    testLocal = true
+    testLocal = (project.findProperty("runanywhere.testLocal") ?: "true")
+        .toString()
+        .toBoolean()

Based on learnings: Applies to */gradle : Kotlin SDK: Use ./gradlew :runanywhere-kotlin:compileDebugKotlinAndroid -Prunanywhere.testLocal=false to build the Android target without requiring Android NDK (uses pre-built JNI libs from GitHub releases)

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
testLocal = true
// =============================================================================
// Version Configuration (MUST match Swift Package.swift and Kotlin build.gradle.kts)
// =============================================================================
genieVersion = "0.3.0"
// =============================================================================
// Remote binary URLs
// RABackendGenie hosted on the public runanywhere-sdks repo
// (runanywhere-genie is private, so binaries are published here)
// =============================================================================
binariesGitHubOrg = "RunanywhereAI"
binariesRepo = "runanywhere-sdks"
binariesBaseUrl = "https://github.com/${binariesGitHubOrg}/${binariesRepo}/releases/download"
// Android native libraries package
genieAndroidUrl = "${binariesBaseUrl}/genie-v${genieVersion}/RABackendGENIE-android-arm64-v8a-v${genieVersion}.zip"
// Helper method to check if we should download
shouldDownloadAndroidLibs = { ->
return !testLocal
}
testLocal = (project.findProperty("runanywhere.testLocal") ?: "true")
.toString()
.toBoolean()
// =============================================================================
// Version Configuration (MUST match Swift Package.swift and Kotlin build.gradle.kts)
// =============================================================================
genieVersion = "0.3.0"
// =============================================================================
// Remote binary URLs
// RABackendGenie hosted on the public runanywhere-sdks repo
// (runanywhere-genie is private, so binaries are published here)
// =============================================================================
binariesGitHubOrg = "RunanywhereAI"
binariesRepo = "runanywhere-sdks"
binariesBaseUrl = "https://github.com/${binariesGitHubOrg}/${binariesRepo}/releases/download"
// Android native libraries package
genieAndroidUrl = "${binariesBaseUrl}/genie-v${genieVersion}/RABackendGENIE-android-arm64-v8a-v${genieVersion}.zip"
// Helper method to check if we should download
shouldDownloadAndroidLibs = { ->
return !testLocal
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere_genie/android/binary_config.gradle`
around lines 14 - 36, The build file hardcodes testLocal = true, preventing
Gradle property overrides; change testLocal to read the Gradle property (e.g.,
project.findProperty("runanywhere.testLocal") or project.hasProperty) and parse
it to a boolean so -Prunanywhere.testLocal=false flips behavior, and ensure
shouldDownloadAndroidLibs (the closure) uses that resolved boolean; update
references to testLocal and keep genieVersion, binariesBaseUrl, and
genieAndroidUrl unchanged.

Comment on lines +31 to +49
genieAndroidUrl = "${binariesBaseUrl}/genie-v${genieVersion}/RABackendGENIE-android-arm64-v8a-v${genieVersion}.zip"

// Helper method to check if we should download
shouldDownloadAndroidLibs = { ->
return !testLocal
}

// Helper method to check if local libs exist
checkLocalLibsExist = { ->
def jniLibsDir = project.file('src/main/jniLibs')
def arm64Dir = new File(jniLibsDir, 'arm64-v8a')

if (!arm64Dir.exists() || !arm64Dir.isDirectory()) {
return false
}

// Check for Genie backend library
def genieLib = new File(arm64Dir, 'librac_backend_genie_jni.so')
return genieLib.exists()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Align binary config with declared ABI filters.

Line 31 and Lines 41-49 are arm64-only, but sdk/runanywhere-flutter/packages/runanywhere_genie/android/build.gradle (Lines 45-48) declares arm64-v8a, armeabi-v7a, and x86_64. This mismatch can produce builds that include ABIs without corresponding Genie .so coverage.

Please either:

  • add URL/validation support for all declared ABIs, or
  • restrict ABI filters to the ABIs actually shipped by Genie binaries.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@sdk/runanywhere-flutter/packages/runanywhere_genie/android/binary_config.gradle`
around lines 31 - 49, The current binary_config uses arm64-only paths/URL
(genieAndroidUrl, checkLocalLibsExist, arm64-v8a, genieLib) while build.gradle
declares arm64-v8a, armeabi-v7a and x86_64; update either side: modify
checkLocalLibsExist and shouldDownloadAndroidLibs to iterate a list of ABIs
(e.g., ['arm64-v8a','armeabi-v7a','x86_64']) and construct per-ABI URLs/paths
(instead of genieAndroidUrl fixed to arm64) and validate each ABI's .so (e.g.,
look for appropriate librac_backend_genie_jni.so under each jniLibsDir/<ABI>),
or alternatively tighten build.gradle's abiFilters to only include 'arm64-v8a'
to match the existing genieAndroidUrl and checks; ensure unique symbols to
change are genieAndroidUrl, shouldDownloadAndroidLibs, and checkLocalLibsExist
so ABI behavior is consistent across config and build.gradle.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant