-
Notifications
You must be signed in to change notification settings - Fork 99
[Kotlin-SDK] [Kotlin-Sample] Android + JVM SDK + Sample apps #47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Updated `build.gradle.kts` to include Kotlin and IntelliJ plugin versions for improved compatibility. - Added `IMPLEMENTATION_SUMMARY.md` and `README.md` to provide comprehensive documentation on the SDK's architecture, features, and usage. - Structured the project with clear module definitions in `settings.gradle.kts`, enhancing maintainability. - Introduced core components for VAD and STT, including `WebRTCVADComponent` and `WhisperSTTComponent`, ensuring a modular design. - Implemented an analytics tracking system in `AnalyticsTracker` for monitoring SDK usage and performance. - Cleaned up unused code and improved adherence to best practices, ensuring a scalable and maintainable solution.
|
Caution Review failedThe pull request is closed. WalkthroughMassive refactor: removed legacy native/JNI/LLM/tokenizer, Room DB, privacy/encryption, and many repositories; added Kotlin Multiplatform SDK integration, model-management stack (repo, viewmodels, Compose UIs), app initialization, Gradle tasks, git submodules, IDE run configs, and extensive docs. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant UI as ModelsScreen
participant VM as ModelManagementViewModel
participant Repo as ModelRepository
participant SDK as RunAnywhere SDK
User->>UI: tap Refresh
UI->>VM: refreshModels()
VM->>Repo: refreshModels()
Repo->>SDK: fetchAvailableModels()
SDK-->>Repo: list<ModelInfo>
Repo-->>VM: update StateFlow(models)
VM-->>UI: UI re-renders from StateFlow
sequenceDiagram
autonumber
actor User
participant ChatUI as ChatScreen
participant ChatVM as ChatViewModel
participant SDK as RunAnywhere SDK
participant Stream as Generation Stream
User->>ChatUI: sendMessage()
ChatUI->>ChatVM: sendMessage()
ChatVM->>SDK: generateStream(prompt)
SDK->>Stream: start streaming tokens
loop streaming
Stream-->>ChatVM: token chunk
ChatVM-->>ChatUI: update assistant partial via StateFlow
end
Stream-->>ChatVM: complete
ChatVM->>ChatUI: update final message + analytics
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Reason: Very large, heterogeneous changes — extensive deletions across native/JNI/LLM/tokenizer/database/privacy/encryption subsystems and many new modules (KMP SDK integration, model repo + UI + viewmodels, multiple new Compose screens, Gradle/submodules/IDE configuration). Each area requires targeted review. Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (12)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- Introduced `BUILD_STATUS.md` to document the current build status, including successful components, build configuration, and next steps for native library compilation. - Added `EXAMPLE_USAGE.kt` to provide a practical example of how to utilize the RunAnywhere STT SDK, demonstrating initialization, model management, and transcription features. - Updated `build.gradle.kts` to streamline plugin and dependency management, ensuring compatibility with the latest Gradle and Kotlin versions. - Cleaned up unused code and improved documentation across the affected files, enhancing clarity and maintainability of the SDK. - Removed obsolete configurations and improved adherence to best practices, ensuring a scalable and maintainable solution.
- Updated `settings.gradle.kts` to include new modules `sdk-core` and `sdk-jni`, enhancing project organization. - Refactored `build.gradle.kts` to replace the deprecated `runanywhere-android` SDK reference with `sdk-core`, ensuring compatibility with the new structure. - Enhanced `MainActivity.kt` to initialize the `RunAnywhereSTT` with updated configuration parameters and added cleanup logic on destruction, improving resource management. - Updated the `AboutScreen` to reflect the new SDK features and supported models, enhancing user information. - Removed obsolete `RunAnywhereSDK` and related files, streamlining the codebase and improving maintainability. - Cleaned up unused code and improved adherence to best practices, ensuring a scalable and maintainable solution.
- Added version catalog support in `settings.gradle.kts` for centralized dependency management, improving maintainability. - Updated `build.gradle.kts` to utilize version catalog for dependencies, ensuring consistency and easier updates. - Replaced deprecated icon references in `EnhancedModelCard.kt` with updated icons, enhancing visual consistency. - Cleaned up unused imports and improved adherence to best practices, ensuring a more scalable and maintainable codebase.
- Updated `build.gradle.kts` to include `META-INF/INDEX.LIST` for improved resource management. - Enhanced `MainActivity.kt` by updating feature descriptions for clarity and consistency. - Removed obsolete files related to conversation summarization, templates, database, and privacy management, streamlining the codebase. - Cleaned up unused imports and improved adherence to best practices, ensuring a more scalable and maintainable solution.
- Updated `.gitignore` to exclude the `EXTERNAL/` directory for cleaner repository management. - Added `IMPLEMENTATION_SUMMARY.md` to provide a comprehensive overview of the RunAnywhere Android SDK architecture and features, ensuring clarity for future development. - Introduced `examples/README.md` to guide users through sample applications demonstrating SDK usage, enhancing onboarding experience. - Created `MainActivity.kt` for the Android STT demo, showcasing real-time speech transcription and voice activity detection, improving practical understanding of the SDK. - Established IntelliJ plugin structure with `VoiceCommandAction` and `VoiceService`, enabling voice command functionality within the IDE, enhancing user interaction. - Cleaned up unused code and improved adherence to best practices, ensuring a scalable and maintainable solution.
- Introduced `ModelInfo` and `ModelInfoRepository` for managing model metadata and persistence, aligning with iOS functionality. - Implemented `WhisperSTTComponent` and `WebRTCVADComponent` using updated libraries for speech-to-text and voice activity detection, enhancing performance and modularity. - Updated `settings.gradle.kts` to include new modules and dependencies for improved project structure and management. - Removed obsolete JNI implementations for Whisper and WebRTC VAD, streamlining the codebase and improving maintainability. - Cleaned up unused code and improved adherence to best practices, ensuring a scalable and maintainable solution.
- Introduced `build-and-run.sh` script to automate the build and deployment process for the RunAnywhere AI Android app, enhancing developer efficiency. - Added comprehensive checks for project directory, connected devices, and APK build success, ensuring a smooth deployment experience. - Implemented model downloading functionality with user prompts for the Whisper model, improving usability and accessibility. - Updated `README.md` to include setup instructions and usage guidelines for the new script, enhancing documentation clarity. - Modified `AndroidManifest.xml` to include necessary permissions for speech-to-text functionality, ensuring compliance with Android requirements. - Cleaned up unused code and improved adherence to best practices, ensuring a scalable and maintainable solution.
…re AI - Added `build_and_install.sh` script to automate the build and installation process for the RunAnywhere AI Android app, improving developer efficiency. - Updated `gradle.properties` to suppress compilation SDK warnings and disable kapt warnings, enhancing build stability. - Modified `settings.gradle.kts` to include JitPack repository for additional library dependencies, improving dependency management. - Adjusted `build.gradle.kts` to update compile and target SDK versions to 35, ensuring compatibility with the latest Android features. - Refactored `AndroidManifest.xml` to change the main activity name to `SimpleMainActivity`, aligning with the new app structure. - Cleaned up unused code and improved adherence to best practices, ensuring a scalable and maintainable solution.
- Deleted the `executorch` and `llama-cpp` feature modules, including their build scripts and associated classes, to streamline the codebase and improve maintainability. - Cleaned up unused code and configurations, ensuring adherence to best practices and enhancing overall project scalability.
- Introduced `MainActivity.kt` to serve as the main entry point for the RunAnywhere AI Android app, featuring real-time speech-to-text functionality and voice activity detection. - Implemented the `STTComponent` for managing speech-to-text operations, including model loading and transcription, aligning with the architecture of the Swift SDK. - Added comprehensive event handling through `EventBus` for SDK-wide event publishing, enhancing modularity and responsiveness. - Created `FileManager` for centralized file management, ensuring efficient handling of model files and cache. - Established a structured logging system with `SDKLogger` for consistent logging across the SDK. - Cleaned up unused code and improved adherence to best practices, ensuring a scalable and maintainable solution.
- Updated `build.gradle.kts` to include the correct SDK module path for `runanywhere-android:core`, ensuring proper dependency management. - Introduced a comprehensive `ANDROID_SAMPLE_APP_IMPL_PLAN.md` to outline the architecture and implementation roadmap for the Android sample app, enhancing clarity for future development. - Enhanced the `kotlin_sdk_stt_mvp_implementation.md` to reflect updated architectural decisions and integration points with the iOS SDK, ensuring alignment across platforms. - Implemented `WhisperSTTService` and `WebRTCVADService` with improved initialization and processing logic, enhancing performance and modularity. - Added `ModelLoadingService` and `DownloadService` for efficient model management and downloading, streamlining the SDK's functionality. - Cleaned up unused code and improved adherence to best practices, ensuring a scalable and maintainable solution.
- Updated `build.gradle.kts` to streamline dependency management by replacing the SDK module path with `sdk-core` and adding the `accompanist.permissions` library for improved permissions handling. - Introduced `RunAnywhereApplication.kt` as the application class for initializing the SDK and managing global configurations, enhancing the app's architecture. - Refactored `MainActivity.kt` to simplify the UI structure and integrate edge-to-edge display support, while removing unused audio recording logic to improve maintainability. - Added new data models including `ChatMessage`, `SpeakerInfo`, `TranscriptSegment`, and `VoiceAudioChunk` to support chat and transcription functionalities, ensuring a robust data handling mechanism. - Implemented the `ChatScreen` and `ChatViewModel` for managing user interactions and message flow, enhancing the chat experience within the app. - Created `TranscriptionScreen` and `VoiceAssistantScreen` for voice interaction capabilities, laying the groundwork for future voice features. - Cleaned up unused code and improved adherence to best practices, ensuring a scalable and maintainable solution.
- Renamed `sdk-jni` to `jni` in `settings.gradle.kts` for consistency and clarity in module naming. - Commented out unused Hilt dependencies in `build.gradle.kts` and removed the `kapt` plugin to streamline the build process. - Updated `MainActivity.kt` and `RunAnywhereApplication.kt` to temporarily disable Hilt integration, addressing compilation issues while maintaining code structure. - Introduced a new `ChatMessage` data model to support chat functionalities, enhancing data handling within the app. - Refactored `ChatScreen` and `ChatViewModel` to improve state management and user interaction, while cleaning up unused code. - Simplified `SettingsScreen` and `StorageScreen` to provide a clearer user interface, indicating upcoming features. - Removed the `MessageBubble` component as part of the cleanup process, ensuring adherence to best practices and improving maintainability. - Cleaned up unused code and improved overall project structure for scalability and maintainability.
- Removed unused imports in `Component.kt`, `VADComponent.kt`, and `RunAnywhere.kt` to streamline code and enhance readability. - Updated `NotInitialized` object in `SDKModels.kt` to a data object for better serialization handling. - Changed the method in `QuantizationLevel` to use `entries` instead of `values()` for improved performance and clarity. - Cleaned up unused code across multiple files, ensuring adherence to best practices and enhancing maintainability.
- Introduced new data models including `AuthenticationModels`, `ConfigurationModels`, `DeviceInfoModels`, `ModelInfoModels`, `TelemetryModels`, and their corresponding services to support authentication, configuration management, device information retrieval, model management, and telemetry tracking. - Implemented repository interfaces for configuration, model info, device info, and telemetry to streamline data handling and improve separation of concerns. - Enhanced `FileManager` for improved file operations and storage management, ensuring efficient handling of model files and cache. - Developed `APIClient` for robust network operations, including authentication and model downloading, with added network connectivity checks. - Established `KeychainManager` for secure token storage, ensuring sensitive data is handled appropriately. - Cleaned up unused code and improved adherence to best practices, ensuring a scalable and maintainable solution.
- Updated `.gitignore` to include new directories for external dependencies and local configurations. - Modified `settings.gradle.kts` to include the JNI module for improved cross-platform functionality. - Enhanced `RunAnywhereAI` build configuration by adding Kotlin Multiplatform support and necessary plugins. - Introduced new data models and services for managing authentication, device info, and telemetry, ensuring a robust architecture. - Implemented database access objects (DAOs) and converters for efficient data handling and storage. - Cleaned up unused code and improved overall project structure for scalability and maintainability.
- Downgraded Kotlin version from 2.2.0 to 2.0.21 for compatibility. - Removed unused compiler arguments from build configurations to streamline the build process. - Introduced new repository implementations for configuration, device info, model info, and telemetry, enhancing data management and separation of concerns. - Added platform-specific VAD service creation methods for Android and JVM, improving modularity. - Cleaned up unused code and improved overall project structure for scalability and maintainability.
- Removed the unused ValidationService from the codebase to streamline functionality. - Updated ModelLoadingService to eliminate validation logic, replacing it with a file existence check for improved clarity and maintainability. - Cleaned up unused imports in ModelRegistry, enhancing code readability and adherence to best practices.
- Updated the SDK version from "1.0.0-SNAPSHOT" to "0.1.0" in the build configuration for clarity and consistency. - Introduced a new build management script (`sdk.sh`) to facilitate building, testing, and publishing the RunAnywhere Kotlin Multiplatform SDK across various platforms, enhancing usability and developer experience. - The script includes comprehensive commands for building, testing, and publishing, along with detailed usage instructions and options for improved functionality. - Cleaned up unused code and ensured adherence to best practices for maintainability and scalability.
- Updated the IntelliJ plugin's `build.gradle.kts` to specify versions for the IntelliJ and Kotlin plugins, enhancing clarity and compatibility. - Introduced a new `gradlew` script for easier Gradle execution across different environments, improving developer experience. - Added a `gradlew.bat` script for Windows compatibility, ensuring cross-platform support. - Created `settings.gradle.kts` to define the root project name and manage plugin repositories, streamlining project structure. - Included `gradle-wrapper.properties` to specify the Gradle distribution, ensuring consistent builds. - Added new actions for testing the RunAnywhere SDK and voice dictation, enhancing plugin functionality. - Cleaned up unused code and comments, adhering to best practices for maintainability and scalability.
- Updated the ModelDownloader class to include model directory management, download progress tracking, and validation checks for downloaded models, improving usability and reliability. - Refactored ModelManager to accept ModelInfo objects instead of model IDs, enhancing type safety and clarity. - Added new enums for LLMFramework and ModelCategory to support additional model types and improve categorization. - Introduced a new JvmNetworkService for handling API calls, ensuring a robust architecture for network operations. - Implemented a comprehensive DownloadService with real HTTP downloading capabilities, including progress tracking and checksum validation. - Added a ValidationService for model file validation, ensuring integrity and format verification. - Cleaned up unused code and improved overall project structure for scalability and maintainability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Introduced a new `THIRD_PARTY_LICENSES.md` file detailing licenses for third-party dependencies, including `llama.cpp` and `MLC-LLM`. - Updated `README.md` to reference the new licenses file and acknowledge third-party contributions. - Enhanced `RELEASE_GUIDE.md` to include steps for verifying third-party licenses in the build process. - Modified `build.gradle.kts` files to ensure inclusion of the licenses in the generated JAR and AAR artifacts. - Added a setup guide for `llama.cpp` as a submodule, improving onboarding for new developers.
- Updated llama.cpp from commit f594bc80 (b3950) to 1faa13a1 (b6725) - Includes 2775 commits of improvements, bug fixes, and new features - Ensures compatibility with latest llama.cpp APIs and optimizations
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (1)
RELEASE_GUIDE.md (1)
170-197: Ensure the SPM tag you test actually exists.Step 2 creates only
ios/v0.14.0, yet the SPM check usesexact: "0.14.0". SwiftPM only resolves SemVer tags, so following the guide verbatim makesswift package resolvefail. Please either push a SemVer tag (v0.14.0/0.14.0) alongside the platform-scoped tag or update the sample to reference the tag you actually push (e.g..revision("ios/v0.14.0")).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
README.md(7 hunks)RELEASE_GUIDE.md(1 hunks)THIRD_PARTY_LICENSES.md(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-10-07T00:42:41.453Z
Learnt from: CR
PR: RunanywhereAI/runanywhere-sdks#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-07T00:42:41.453Z
Learning: Applies to sdk/runanywhere-android/**/build.gradle{,.kts} : Use Kotlin version 2.0.21 in the Android SDK
Applied to files:
README.md
📚 Learning: 2025-10-07T00:42:41.453Z
Learnt from: CR
PR: RunanywhereAI/runanywhere-sdks#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-07T00:42:41.453Z
Learning: Applies to sdk/runanywhere-android/**/RunAnywhereSDK.kt : Expose the Android SDK main entry via RunAnywhereSDK.getInstance() (singleton)
Applied to files:
README.md
📚 Learning: 2025-10-07T00:42:41.453Z
Learnt from: CR
PR: RunanywhereAI/runanywhere-sdks#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-07T00:42:41.453Z
Learning: Applies to sdk/runanywhere-android/**/*.kt : Use Kotlin coroutines for async APIs in the Android SDK
Applied to files:
README.md
🪛 markdownlint-cli2 (0.18.1)
RELEASE_GUIDE.md
460-460: Bare URL used
(MD034, no-bare-urls)
THIRD_PARTY_LICENSES.md
16-16: Bare URL used
(MD034, no-bare-urls)
23-23: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
52-52: Bare URL used
(MD034, no-bare-urls)
59-59: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
265-265: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
313-313: Bare URL used
(MD034, no-bare-urls)
🔇 Additional comments (1)
README.md (1)
238-241: Confirm Kotlin toolchain requirementSystem requirements now state Kotlin 2.1.21+, but we’ve been standardizing on 2.0.21 in the Android SDK. Please verify the actual Gradle configuration and align the docs (or bump the build scripts accordingly) so users aren’t misled.
Based on learnings
| ### ✅ Android SDK - **Available** | ||
| The Android Kotlin Multiplatform SDK provides high-performance on-device text generation with streaming support, comprehensive model management, structured outputs with JSON generation, and thinking model support for privacy-first AI applications. [View Android SDK →](sdk/runanywhere-kotlin/) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Align Android availability badge with body text
Top-level badge still says “Android SDK — Coming Soon,” but this section declares the Android SDK as available. Please update the badge so readers aren’t told two different statuses.
🤖 Prompt for AI Agents
In README.md around lines 19-20, the top-level Android SDK badge still reads
“Coming Soon” while the section here marks the Android SDK as “Available”;
update the badge text/status to match the body by changing the badge to indicate
availability (e.g., “Android SDK — Available” or equivalent), and ensure any
badge link or alt text matches the section copy so both places consistently
state the SDK is available.
| import com.runanywhere.sdk.public.RunAnywhere | ||
| import com.runanywhere.sdk.llm.llamacpp.LlamaCppModule | ||
| import com.runanywhere.sdk.models.RunAnywhereGenerationOptions | ||
|
|
||
| // 1. Initialize the SDK | ||
| suspend fun initializeSDK() { | ||
| // Register LlamaCpp module for GGUF model support | ||
| LlamaCppModule.register() | ||
|
|
||
| // Initialize SDK | ||
| RunAnywhere.initialize( | ||
| apiKey = "dev", // Any string works in dev mode | ||
| baseURL = "https://api.runanywhere.ai", | ||
| environment = SDKEnvironment.DEVELOPMENT | ||
| ) | ||
| } | ||
|
|
||
| // 2. Download and load model | ||
| suspend fun setupModel() { | ||
| // Download model with progress tracking | ||
| RunAnywhere.downloadModel("smollm2-360m").collect { progress -> | ||
| println("Download progress: ${(progress * 100).toInt()}%") | ||
| } | ||
|
|
||
| // Load model | ||
| val success = RunAnywhere.loadModel("smollm2-360m") | ||
| if (success) { | ||
| println("Model loaded successfully") | ||
| } | ||
| } | ||
|
|
||
| // 3. Generate text (non-streaming) | ||
| suspend fun generateText() { | ||
| val result = RunAnywhere.generate( | ||
| prompt = "Explain quantum computing in simple terms", | ||
| options = RunAnywhereGenerationOptions( | ||
| maxTokens = 100, | ||
| temperature = 0.7f | ||
| ) | ||
| ) | ||
| println("Generated: $result") | ||
| } | ||
|
|
||
| // 4. Generate text with streaming | ||
| suspend fun streamText() { | ||
| RunAnywhere.generateStream( | ||
| prompt = "Explain quantum computing in simple terms", | ||
| options = RunAnywhereGenerationOptions( | ||
| maxTokens = 100, | ||
| temperature = 0.7f | ||
| ) | ||
| ).collect { token -> | ||
| print(token) // Print each token as it arrives | ||
| } | ||
| } | ||
|
|
||
| // 5. Get current model info | ||
| val currentModel = RunAnywhere.currentModel | ||
| println("Current model: ${currentModel?.name}") | ||
|
|
||
| // 6. Unload model when done | ||
| suspend fun cleanup() { | ||
| RunAnywhere.unloadModel() | ||
| } | ||
| ``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add the missing SDKEnvironment import
The quick-start snippet references SDKEnvironment but doesn’t import it, so copy-pasting the sample won’t compile. Please include the import to keep the example runnable.
import com.runanywhere.sdk.public.RunAnywhere
import com.runanywhere.sdk.llm.llamacpp.LlamaCppModule
import com.runanywhere.sdk.models.RunAnywhereGenerationOptions
+import com.runanywhere.sdk.public.SDKEnvironment📝 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.
| import com.runanywhere.sdk.public.RunAnywhere | |
| import com.runanywhere.sdk.llm.llamacpp.LlamaCppModule | |
| import com.runanywhere.sdk.models.RunAnywhereGenerationOptions | |
| // 1. Initialize the SDK | |
| suspend fun initializeSDK() { | |
| // Register LlamaCpp module for GGUF model support | |
| LlamaCppModule.register() | |
| // Initialize SDK | |
| RunAnywhere.initialize( | |
| apiKey = "dev", // Any string works in dev mode | |
| baseURL = "https://api.runanywhere.ai", | |
| environment = SDKEnvironment.DEVELOPMENT | |
| ) | |
| } | |
| // 2. Download and load model | |
| suspend fun setupModel() { | |
| // Download model with progress tracking | |
| RunAnywhere.downloadModel("smollm2-360m").collect { progress -> | |
| println("Download progress: ${(progress * 100).toInt()}%") | |
| } | |
| // Load model | |
| val success = RunAnywhere.loadModel("smollm2-360m") | |
| if (success) { | |
| println("Model loaded successfully") | |
| } | |
| } | |
| // 3. Generate text (non-streaming) | |
| suspend fun generateText() { | |
| val result = RunAnywhere.generate( | |
| prompt = "Explain quantum computing in simple terms", | |
| options = RunAnywhereGenerationOptions( | |
| maxTokens = 100, | |
| temperature = 0.7f | |
| ) | |
| ) | |
| println("Generated: $result") | |
| } | |
| // 4. Generate text with streaming | |
| suspend fun streamText() { | |
| RunAnywhere.generateStream( | |
| prompt = "Explain quantum computing in simple terms", | |
| options = RunAnywhereGenerationOptions( | |
| maxTokens = 100, | |
| temperature = 0.7f | |
| ) | |
| ).collect { token -> | |
| print(token) // Print each token as it arrives | |
| } | |
| } | |
| // 5. Get current model info | |
| val currentModel = RunAnywhere.currentModel | |
| println("Current model: ${currentModel?.name}") | |
| // 6. Unload model when done | |
| suspend fun cleanup() { | |
| RunAnywhere.unloadModel() | |
| } | |
| ``` | |
| import com.runanywhere.sdk.public.RunAnywhere | |
| import com.runanywhere.sdk.llm.llamacpp.LlamaCppModule | |
| import com.runanywhere.sdk.models.RunAnywhereGenerationOptions | |
| import com.runanywhere.sdk.public.SDKEnvironment |
🤖 Prompt for AI Agents
In README.md around lines 160 to 224, the sample Kotlin snippet uses
SDKEnvironment but the import is missing; add the import for SDKEnvironment
(e.g., import com.runanywhere.sdk.public.SDKEnvironment) alongside the other
imports so the example compiles when copy-pasted.
There was a problem hiding this 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
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
.idea/vcs.xml(1 hunks)THIRD_PARTY_LICENSES.md(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- .idea/vcs.xml
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
THIRD_PARTY_LICENSES.md
16-16: Bare URL used
(MD034, no-bare-urls)
23-23: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
52-52: Bare URL used
(MD034, no-bare-urls)
59-59: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
265-265: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
330-330: Bare URL used
(MD034, no-bare-urls)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (5)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/data/ModelInfo.kt (1)
49-56:updateState()computes derived state but doesn't update the instance.This issue was flagged in a previous review. The function computes what the state should be but doesn't modify the
statefield. However, sincestateis avalin the data class (line 16), it cannot be mutated in place.The function name is misleading—it should either:
- Return a copy of
ModelUiStatewith the updated state:return copy(state = when { ... })- Be renamed to
computeState()orderiveState()to reflect that it only calculates without mutatingSince this appears to compute derived state, consider whether the
statefield should be a computed property instead of a stored field, or ensure callers usemodel.copy(state = model.updateState())when they need to update state.examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/repository/ModelRepository.kt (1)
170-172: Avoid restricted external storage API on Android 11+.This issue was flagged in a previous review. Line 170 uses
Environment.getExternalStorageDirectory()which requiresMANAGE_EXTERNAL_STORAGEpermission on Android 11+ and will throwSecurityExceptionon modern devices.Use an app-accessible directory instead:
- val externalStatFs = StatFs(Environment.getExternalStorageDirectory().path) + val externalDir = context.getExternalFilesDir(null) + ?: context.filesDir // Fallback to internal storage + val externalStatFs = StatFs(externalDir.path)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/ui/ModelsScreen.kt (3)
36-39: Use application context to prevent memory leak.This issue was flagged in a previous review. Line 38 passes the Activity context to
ModelRepository, causing the ViewModel to hold a reference to the Activity. This prevents the Activity from being garbage collected on configuration changes (e.g., rotation).Apply the fix:
val context = LocalContext.current val viewModel: ModelManagementViewModel = viewModel { - ModelManagementViewModel(ModelRepository(context)) + ModelManagementViewModel(ModelRepository(context.applicationContext)) }
174-183: Implement or hide the Add Model dialog.This issue was flagged in a previous review. Lines 178-181 show the
AddModelDialogcallback is marked TODO and discards user input. Since the UI is exposed to users, it should either be fully implemented or hidden until ready.Consider:
- Implementing the feature by adding an
addModelFromUrl()method to the ViewModel- Temporarily hiding the "Add" button (line 70) until the feature is complete
606-611: Implement or remove non-functional Cancel button.This issue was flagged in a previous review. Line 607 renders a "Cancel" button during downloads, but its
onClickhandler is empty. Users expect this to abort the download, but nothing happens.Either:
- Wire it to a cancellation callback from the ViewModel
- Remove the button until cancellation is supported
🧹 Nitpick comments (7)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/ui/ModelDialogs.kt (2)
393-416: Consider filtering framework options for custom models.Line 393 iterates over all
LLMFramework.values(), but certain frameworks likeFOUNDATION_MODELSrepresent built-in system models and shouldn't be selectable when adding custom models from URLs. Consider filtering the list to only show frameworks that support downloadable custom models.Apply this diff to filter the frameworks:
- LLMFramework.values().forEach { framework -> + LLMFramework.values() + .filter { it != LLMFramework.FOUNDATION_MODELS } + .forEach { framework -> DropdownMenuItem(
363-371: Consider adding URL format validation.The URL field accepts any non-blank string without validating the format. Consider adding basic URL validation to provide better user feedback before attempting a download.
You could add a validation function:
fun isValidUrl(url: String): Boolean { return url.startsWith("http://") || url.startsWith("https://") }Then update the button's enabled state:
- enabled = modelName.isNotBlank() && modelUrl.isNotBlank() + enabled = modelName.isNotBlank() && modelUrl.isNotBlank() && isValidUrl(modelUrl)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/repository/ModelRepository.kt (3)
55-57: Consider logging exceptions before returning empty list.The catch block silently swallows exceptions, which static analysis flagged. While returning an empty list is a reasonable fallback, logging the exception would help diagnose SDK initialization or network issues during development and debugging.
Add logging:
} catch (e: Exception) { + // Log for debugging: logger.warn("Failed to refresh models", e) // If SDK not initialized or fails, show empty list _availableModels.value = emptyList()
91-94: Verify downloaded file existence before updating state.Lines 91-94 construct the local path and update the model state to DOWNLOADED without verifying that the file actually exists on disk. If the SDK reports success but the file wasn't created (e.g., due to permission issues or disk full), the state will be inconsistent.
Add file existence verification:
// Update model state and path val localPath = File(modelsDir, "${modelId}${model.format.extension}").absolutePath + if (!File(localPath).exists()) { + throw IllegalStateException("Download succeeded but file not found at $localPath") + } updateModelPath(modelId, localPath) updateModelState(modelId, ModelState.DOWNLOADED)
142-147: Handle file deletion failures gracefully.Lines 142-147 attempt to delete the model file but don't check if deletion succeeded. If
file.delete()returns false (e.g., due to file locks or permissions), the repository state will be updated to AVAILABLE even though the file still exists on disk, causing inconsistency.Add deletion verification:
model.localPath?.let { path -> val file = File(path) if (file.exists()) { - file.delete() + if (!file.delete()) { + throw IOException("Failed to delete model file at $path") + } } }examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/viewmodel/ModelManagementViewModel.kt (1)
20-29: Consider logging models with null preferredFramework.Line 23 silently falls back to
LLMFramework.LLAMA_CPPwhen a model'spreferredFrameworkis null. This might hide data quality issues. Consider logging these cases during development to identify models with missing framework metadata.examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/ui/ModelsScreen.kt (1)
80-86: Storage FAB navigation is not implemented.Line 82 shows the Storage FAB's
onClickis empty with a TODO comment. While less critical than the other TODOs, users might expect this button to navigate to storage management. Consider either implementing the navigation or removing the FAB until the storage screen is ready.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (10)
.idea/vcs.xml(1 hunks)build.gradle.kts(1 hunks)examples/android/RunAnywhereAI/app/build.gradle.kts(4 hunks)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/data/ModelInfo.kt(1 hunks)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/data/StorageInfo.kt(1 hunks)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/repository/ModelRepository.kt(1 hunks)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/ui/ModelDialogs.kt(1 hunks)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/ui/ModelUiExtensions.kt(1 hunks)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/ui/ModelsScreen.kt(1 hunks)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/viewmodel/ModelManagementViewModel.kt(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- .idea/vcs.xml
🚧 Files skipped from review as they are similar to previous changes (2)
- build.gradle.kts
- examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/data/StorageInfo.kt
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-07T00:42:41.855Z
Learnt from: CR
PR: RunanywhereAI/runanywhere-sdks#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-07T00:42:41.855Z
Learning: Applies to sdk/runanywhere-android/**/build.gradle{,.kts} : Use Kotlin version 2.0.21 in the Android SDK
Applied to files:
examples/android/RunAnywhereAI/app/build.gradle.kts
📚 Learning: 2025-10-07T00:42:41.855Z
Learnt from: CR
PR: RunanywhereAI/runanywhere-sdks#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-07T00:42:41.855Z
Learning: Applies to sdk/runanywhere-android/**/build.gradle{,.kts} : Android minSdk must be 24 and targetSdk must be 36
Applied to files:
examples/android/RunAnywhereAI/app/build.gradle.kts
🧬 Code graph analysis (3)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/viewmodel/ModelManagementViewModel.kt (1)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/repository/ModelRepository.kt (1)
refreshModels(43-61)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/repository/ModelRepository.kt (2)
sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/services/modelinfo/ModelInfoService.kt (1)
updateDownloadProgress(180-198)sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/models/ModelManager.kt (1)
calculateDirectorySize(212-235)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/ui/ModelsScreen.kt (1)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/ui/ModelDialogs.kt (2)
ModelDetailsDialog(24-299)AddModelDialog(319-486)
🪛 detekt (1.23.8)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/repository/ModelRepository.kt
[warning] 55-55: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/data/ModelInfo.kt
[warning] 62-62: String.format("%.1f KB", bytes / 1024.0) uses implicitly default locale for string formatting.
(detekt.potential-bugs.ImplicitDefaultLocale)
[warning] 63-63: String.format("%.1f MB", bytes / (1024.0 * 1024)) uses implicitly default locale for string formatting.
(detekt.potential-bugs.ImplicitDefaultLocale)
[warning] 64-64: String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024)) uses implicitly default locale for string formatting.
(detekt.potential-bugs.ImplicitDefaultLocale)
🔇 Additional comments (8)
examples/android/RunAnywhereAI/app/build.gradle.kts (6)
118-119: Packaging exclusions are appropriate.The additions of
META-INF/*.kotlin_moduleandMETA-INF/INDEX.LISTto packaging exclusions are appropriate for a multi-module project with extensive dependencies. These prevent conflicts from duplicate Kotlin metadata and index files.
314-335: Kotlin version constraints are well-structured.The constraints block correctly enforces consistent Kotlin standard library versions across all variants (stdlib, stdlib-jdk7, stdlib-jdk8, kotlin-reflect), using
strictly()to prevent version skew. This aligns with the learnings requirement to use Kotlin 2.0.21.
142-146: Java 17 toolchain configuration is consistent and appropriate.The alignment of
sourceCompatibility,targetCompatibility, andjvmTargetto Java 17 is correct and consistent. This is a modern, LTS-supported target for Android development.
195-310: Dependency organization is well-structured and comprehensive.The dependencies block is logically organized with clear sections (SDK, AndroidX, Compose, Networking, etc.) and consistently uses the version catalog. This makes maintenance easier and aligns with the PR's goals for audio processing, speech recognition, and LLM integration.
38-81: Build type configuration is solid.The debug, release, and benchmark variants are properly configured with appropriate minification, debugging, and build config fields. The benchmark variant is a nice addition for performance profiling.
199-200: ****The module reference at line 200 is correct. The nested path
:sdk:runanywhere-kotlin:modules:runanywhere-llm-llamacppproperly matches the module declaration inexamples/android/RunAnywhereAI/settings.gradle.kts:39. This follows standard Gradle multi-module project structure where sub-modules are declared with their full paths relative to the root settings file.Likely an incorrect or invalid review comment.
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/viewmodel/ModelManagementViewModel.kt (1)
141-155: Verify if manual refresh is needed after delete.Line 148 calls
refreshModels()after deleting a model. If the repository's_availableModelsStateFlow is already updated bydeleteModel(), this refresh might be unnecessary. Verify whether the repository automatically updates the flow or if this manual refresh is required.examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/ui/ModelDialogs.kt (1)
82-82: The review comment is incorrect. ModelCategory already has displayName as a direct enum property.The verification confirms that
ModelCategoryinsdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/models/enums/ModelCategory.ktis an enum class withdisplayNamedefined as a constructor parameter on line 12, not as an extension. Each enum value (e.g.,LANGUAGE("language", "Language Model", "text_bubble")) hasdisplayNamebuilt-in. The codemodel.category.displayNameon line 82 will compile and work without error.Likely an incorrect or invalid review comment.
| "-Xjvm-default=all", | ||
| "-Xskip-metadata-version-check" // Skip version check to avoid conflicts |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify the necessity and implications of aggressive compiler flags.
These flags suppress critical version checks and alter binary compatibility:
-Xjvm-default=all: Changes interface default method handling, potentially causing binary incompatibility across dependencies.-Xskip-metadata-version-check: Masks metadata version conflicts that could cause runtime failures.
Clarify why these flags are necessary. If dependencies have version mismatches, address the root cause rather than suppressing checks.
Please confirm:
- Is
-Xjvm-default=allrequired for SDK or dependency compatibility? - Why is metadata version checking skipped? Are there known version conflicts?
🌐 Web query:
What is the impact of -Xjvm-default=all in Kotlin compiler?
💡 Result:
Summary — what -Xjvm-default=all does
- Compiles every non-abstract Kotlin interface member as a JVM default method (Java 8+), instead of putting implementations into DefaultImpls helper classes. [1][2]
- Result: concrete implementation lives on the interface as a Java default method; classes implementing the interface can inherit the implementation from the interface. [2]
Main impacts and risks
- Binary compatibility: switching to this mode can be binary‑incompatible with code compiled under the old scheme (clients that expect DefaultImpls). Use compatibility modes or recompile dependent modules to avoid NoSuchMethodError. [1][2][5]
- Smaller/cleaner bytecode when used without compatibility, but fewer compatibility bridges; with all-compatibility mode the compiler emits extra bridge code to preserve compatibility at the cost of larger bytecode. [2]
- Affects delegation and how super calls to interface implementations are compiled. [2]
- Deprecated/changed over time: the experimental -Xjvm-default family was later stabilized and replaced by the -jvm-default option with modes (enable / no-compatibility / disable) in newer Kotlin versions — check your Kotlin version and prefer the stable setting. [3][8]
If you need concrete advice for your project (compatibility strategy, Gradle/Kotlin DSL setting, or migration steps), tell me your Kotlin version and whether you control all modules and I’ll give specific steps and examples.
Sources
[1] Kotlin blog: Generating default methods in interfaces (Kotlin 1.4 announcement).
[2] Kotlin docs: What's new in Kotlin 1.6.20 (notes on -Xjvm-default=all and @JvmDefaultWithCompatibility).
[3] Kotlin docs: Kotlin 2.2 / -jvm-default stable option (replacement for -Xjvm-default).
[4] Kotlin docs / compatibility guidance on binary compatibility and default methods.
[/run_scripts]
🏁 Script executed:
#!/bin/bash
# Find Kotlin version and compiler flag context in build.gradle.kts
rg -n "kotlin.*=|kotlin(" examples/android/RunAnywhereAI/app/build.gradle.kts -A2 -B2
echo "---"
# Search for context around the compiler flags
rg -n "Xjvm-default|Xskip-metadata" examples/android/RunAnywhereAI/app/build.gradle.kts -B5 -A5Length of output: 643
[/run_scripts]
🏁 Script executed:
#!/bin/bash
# Search for Kotlin version more broadly
rg -n "version.*kotlin|kotlin.*version" examples/android/RunAnywhereAI/app/build.gradle.kts -i
echo "---"
# Check plugins block
rg -n "plugins\s*{" examples/android/RunAnywhereAI/app/build.gradle.kts -A20
echo "---"
# Look for kotlin() plugin version
head -50 examples/android/RunAnywhereAI/app/build.gradle.ktsLength of output: 1862
[/run_scripts]
🏁 Script executed:
#!/bin/bash
# Find version catalog file
fd "libs.versions.toml" examples/android/RunAnywhereAI/
echo "---"
# Also check for gradle/libs.versions.toml in root
fd "libs.versions.toml"Length of output: 78
[/run_scripts]
🏁 Script executed:
#!/bin/bash
# Read the root version catalog to find Kotlin version
cat gradle/libs.versions.toml | grep -i kotlinLength of output: 1524
[/web_search]
🌐 Web query:
Kotlin 2.1.0 -jvm-default stable option available
💡 Result:
Short answer: No — the stable -jvm-default option arrives in Kotlin 2.2.0. In Kotlin 2.1.0 the new stable jvmDefault compiler option is not available; the older experimental/deprecated -Xjvm-default modes are what existed prior to 2.2.0. [1][2]
Sources:
[1] What's new in Kotlin 2.1.0 — Kotlin docs.
[2] What's new in Kotlin 2.2.0 — Kotlin docs (introduces stable -jvm-default / jvmDefault option).
Verify necessity and implications of experimental compiler flags in Kotlin 2.1.0.
The -Xjvm-default=all flag is experimental and deprecated, with a stable replacement arriving in Kotlin 2.2.0. Using this flag changes interface compilation and creates binary compatibility risks with code expecting DefaultImpls helper classes. Additionally, -Xskip-metadata-version-check suppresses version validation.
Given Kotlin 2.1.0:
- Justify why
-Xjvm-default=allis necessary. If compatibility is the goal, plan migration to Kotlin 2.2.0+ to use the stable-jvm-defaultoption instead. - Clarify why metadata version checking is skipped—if there are known SDK or dependency conflicts, address the root cause rather than suppressing the check.
🤖 Prompt for AI Agents
In examples/android/RunAnywhereAI/app/build.gradle.kts around lines 154-155, the
build uses the experimental flags "-Xjvm-default=all" and
"-Xskip-metadata-version-check"; validate and either remove or document and plan
migration: if you need JVM default implementations for binary compatibility, add
a concise justification comment here and open a follow-up task to upgrade to
Kotlin 2.2+ and switch to the stable -jvm-default option; if metadata version
checks were disabled to mask dependency/SDK mismatches, revert removal of the
check and instead fix the underlying dependency or toolchain causing the
mismatch (or add explicit compatibility shims), then run full integration and
ABI tests to confirm no runtime breakage before keeping any override.
|
|
||
| composeOptions { | ||
| kotlinCompilerExtensionVersion = "1.5.8" | ||
| kotlinCompilerExtensionVersion = "1.5.15" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify Compose compiler extension version compatibility with Kotlin 2.0.21.
The Compose extension version 1.5.15 was designed for Kotlin 1.9.x. Per learnings, the project uses Kotlin 2.0.21, which may require an updated Compose compiler extension version.
🌐 Web query:
What Compose compiler extension version is compatible with Kotlin 2.0.21?
💡 Result:
Use Compose compiler extension version 2.0.21 (match the Kotlin version). [1][2]
Sources:
[1] Android Developers — Upgrade dependency versions (Compose compiler guidance).
[2] Gradle Plugin Registry — org.jetbrains.kotlin.plugin.compose version 2.0.21.
Update Compose compiler extension version from 1.5.15 to 2.0.21.
The Compose compiler extension version must match the Kotlin version; for Kotlin 2.0.21, use Compose compiler extension version 2.0.21. The current version 1.5.15 is incompatible and designed for Kotlin 1.9.x.
🤖 Prompt for AI Agents
In examples/android/RunAnywhereAI/app/build.gradle.kts around line 160, the
kotlinCompilerExtensionVersion is set to "1.5.15" which is incompatible with
Kotlin 2.0.21; update that value to "2.0.21" so the Compose compiler extension
matches the project's Kotlin version, and verify the project's Kotlin
toolchain/version is set to 2.0.21 to ensure compatibility.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 12
🧹 Nitpick comments (23)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/domain/models/SessionState.kt (1)
8-16: Well-structured enum for voice session lifecycle.The enum clearly represents the states of a voice interaction session. The states are logically ordered and cover the expected lifecycle.
Consider documenting valid state transitions in the KDoc to help developers understand the expected flow and prevent invalid transitions. For example:
/** * Session states for voice interaction * UI-specific enum that tracks the current state of the voice assistant * Matches iOS VoiceAssistantViewModel.SessionState * * Valid transitions: * - DISCONNECTED -> CONNECTING * - CONNECTING -> CONNECTED | ERROR * - CONNECTED -> LISTENING | DISCONNECTED * - LISTENING -> PROCESSING | ERROR * - PROCESSING -> SPEAKING | ERROR * - SPEAKING -> LISTENING | DISCONNECTED * - ERROR -> DISCONNECTED */examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/domain/models/SpeakerInfo.kt (1)
11-16: Consider using Int instead of Long for the color property.Since
toArgb()returns anInt(line 50), storing color asLongrequires an extra conversion and uses more memory without apparent benefit.Apply this diff to simplify the type:
data class SpeakerInfo( val id: String, val name: String, val confidence: Float, - val color: Long // Color for UI display + val color: Int // Color for UI display (ARGB) ) {And update line 50:
- return colors[colorIndex].toArgb().toLong() + return colors[colorIndex].toArgb()examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/repository/ModelRepository.kt (4)
54-56: Log the caught exception.The exception is swallowed without logging, making it difficult to diagnose issues when the SDK fails to initialize or return models. While returning an empty list is reasonable for the UI, developers should be able to see what went wrong.
Apply this diff to add logging:
} catch (e: Exception) { + android.util.Log.e("ModelRepository", "Failed to refresh models", e) // If SDK not initialized or fails, show empty list _availableModels.value = emptyList()
169-185: Good fix for SecurityException, but add logging for caught exceptions.The implementation now correctly uses app-accessible directories (
getExternalFilesDir,externalMediaDirs) to avoid the SecurityException issue flagged in the previous review. However, the caught exceptions at lines 179 and 182 are swallowed without logging, making it harder to diagnose storage computation issues.Apply this diff to add logging:
} catch (e: SecurityException) { + android.util.Log.w("ModelRepository", "SecurityException accessing storage stats, falling back to internal", e) // If we still hit a SecurityException, fall back to internal storage stats Pair(totalAppStorage, availableAppStorage) } catch (e: IllegalArgumentException) { + android.util.Log.w("ModelRepository", "Invalid path for storage stats, falling back to internal", e) // Handle invalid path Pair(totalAppStorage, availableAppStorage)
252-262: Add exception handling to prevent crashes.The
calculateDirectorySizefunction lacks exception handling, which could cause crashes if file access fails. The SDK'sModelManager.calculateDirectorySize(lines 211-234) demonstrates a more robust approach with try-catch and fallback to 0L.Apply this diff to add exception handling:
private fun calculateDirectorySize(dir: File): Long { + return try { - var size = 0L - if (dir.exists() && dir.isDirectory) { - dir.walkTopDown().forEach { file -> - if (file.isFile) { - size += file.length() + var size = 0L + if (dir.exists() && dir.isDirectory) { + dir.walkTopDown().forEach { file -> + if (file.isFile) { + size += file.length() + } } } + size + } catch (e: Exception) { + android.util.Log.w("ModelRepository", "Failed to calculate directory size for ${dir.path}", e) + 0L } - return size }
62-102: Consider adding integrity verification.The download flow lacks SHA-256 hash verification. The related
ModelRepositoryEnhanced(lines 24-160) demonstrates hash verification usingcalculateFileHashafter download completion. Adding integrity checks would help ensure downloaded models aren't corrupted.examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/RunAnywhereApplication.kt (2)
27-36: Consider using a dedicated CoroutineScope instead of GlobalScope.While
GlobalScopeis tolerable forApplication.onCreate()(since the Application lifetime matches the app's lifetime), structured concurrency is preferred. Consider creating a privateCoroutineScopewith aSupervisorJobtied to the Application lifecycle, which can be cancelled inonTerminate()if needed.Example:
private val applicationScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) override fun onCreate() { super.onCreate() Log.i("RunAnywhereApp", "🏁 App launched, initializing SDK...") applicationScope.launch { initializeSDK() } }
145-234: Refactor repetitive model registrations to a data-driven approach.The eight model registration blocks are highly repetitive. Consider extracting model metadata into a data structure and iterating over it to reduce code duplication and improve maintainability.
Example:
private data class ModelConfig( val url: String, val name: String, val type: String, val logName: String = name ) private suspend fun registerModelsForDevelopment() { Log.i("RunAnywhereApp", "📦 Registering models for DEVELOPMENT mode") val models = listOf( ModelConfig( url = "https://huggingface.co/prithivMLmods/SmolLM2-360M-GGUF/resolve/main/SmolLM2-360M.Q8_0.gguf", name = "SmolLM2 360M Q8_0", type = "LLM" ), ModelConfig( url = "https://huggingface.co/Triangle104/Qwen2.5-0.5B-Instruct-Q6_K-GGUF/resolve/main/qwen2.5-0.5b-instruct-q6_k.gguf", name = "Qwen 2.5 0.5B Instruct Q6_K", type = "LLM" ), // ... add remaining models ) try { models.forEach { model -> addModelFromURL( url = model.url, name = model.name, type = model.type ) Log.i("RunAnywhereApp", "✅ Registered: ${model.logName}") } Log.i("RunAnywhereApp", "🎉 All models registered for development (lazy loading enabled)") Log.i("RunAnywhereApp", "🔍 Scanning for previously downloaded models...") RunAnywhere.scanForDownloadedModels() Log.i("RunAnywhereApp", "✅ File system scan complete") } catch (e: Exception) { Log.e("RunAnywhereApp", "❌ Failed to register models: ${e.message}") } }examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/domain/services/AudioCaptureService.kt (1)
36-39: Consider removing unused SAMPLE_RATE constant.The
SAMPLE_RATEconstant is defined but never used within this class. Since it's private, external code cannot reference it for backward compatibility.Apply this diff to remove the unused constant:
- companion object { - // Constants kept for backward compatibility - private const val SAMPLE_RATE = 16000 // 16kHz for Whisper compatibility - }examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelSelectionViewModel.kt (3)
101-110: AvoidprintStackTrace()and rethrow coroutine cancellations.Swallowing
CancellationExceptioncan break cooperative cancellation, andprintStackTrace()is noisy.Apply:
- } catch (e: Exception) { - android.util.Log.e("ModelSelectionVM", "❌ Failed to load models: ${e.message}", e) - e.printStackTrace() + } catch (e: kotlinx.coroutines.CancellationException) { + throw e + } catch (e: Exception) { + android.util.Log.e("ModelSelectionVM", "❌ Failed to load models: ${e.message}", e)
35-41: Minor: simplify import usage and remove unused imports.
android.os.Buildandkotlin.collections.findare unnecessary.- Use the imported
DeviceInfotype inModelSelectionUiStateinstead of FQCN to avoid duplicate imports.Apply:
-import android.os.Build @@ -import kotlin.collections.find +import kotlinx.coroutines.CancellationExceptionAnd below:
-data class ModelSelectionUiState( - val deviceInfo: com.runanywhere.sdk.models.DeviceInfo? = null, +data class ModelSelectionUiState( + val deviceInfo: DeviceInfo? = null,
49-89: Excessive verbose logging in production paths.Consider gating detailed logs behind
BuildConfig.DEBUGor a logger with levels to avoid log noise and overhead in release builds.examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelSelectionBottomSheet.kt (6)
472-472: Locale-stable casing.
uppercase()is locale-sensitive (e.g., Turkish). UseLocale.ROOT.- ModelBadge(text = model.format.name.uppercase()) + ModelBadge(text = model.format.name.uppercase(Locale.ROOT))Add
import java.util.Locale.
665-673: Specify Locale explicitly in String.format.Detekt warns about implicit default locale. Use
Locale.getDefault()(user-facing) orLocale.ROOT.-private fun formatBytes(bytes: Long): String { +private fun formatBytes(bytes: Long): String { val gb = bytes / (1024.0 * 1024.0 * 1024.0) return if (gb >= 1.0) { - String.format("%.2f GB", gb) + String.format(java.util.Locale.getDefault(), "%.2f GB", gb) } else { val mb = bytes / (1024.0 * 1024.0) - String.format("%.0f MB", mb) + String.format(java.util.Locale.getDefault(), "%.0f MB", mb) } }
401-418:frameworkparameter unused in EmptyModelsMessage.Either use it in the message or remove the param.
-private fun EmptyModelsMessage(framework: String) { +private fun EmptyModelsMessage(framework: String) { @@ - Text( - text = "No models available for this framework", + Text( + text = "No models available for $framework", style = AppTypography.caption, color = MaterialTheme.colorScheme.onSurfaceVariant )
620-659: Loading overlay doesn’t show the model name.You accept
modelNamebut never render it. Consider including it for clarity.- Text( - text = "Loading Model", + Text( + text = "Loading $modelName", style = MaterialTheme.typography.headlineMedium, fontWeight = FontWeight.Bold )
585-614: Duplicate UI and utilities (ModelBadge, formatBytes) across files.There are multiple
ModelBadge/formatBytesvariants in this package and in ModelsScreen. Extract to a shared UI/util module to avoid drift and inconsistencies.
- Create
presentation/components/ModelBadge.kt- Create
presentation/util/Format.ktwith a singleformatBytes(Long)using explicit LocaleAlso applies to: 665-673
139-146: Reduce noisy debug logs in recomposition paths.These logs run on every recomposition and can spam Logcat. Remove or guard with a debug flag.
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelsScreen.kt (5)
31-31: Use lifecycle-aware state collection.Prefer
collectAsStateWithLifecycle()to avoid updates while the screen is not started/resumed.- val uiState by viewModel.uiState.collectAsState() + val uiState by viewModel.uiState.collectAsStateWithLifecycle()Add
import androidx.lifecycle.compose.collectAsStateWithLifecycle.
225-236: Color semantics: avoid using error colors for model count.
errorContainer/onErrorContainerreads as a warning. Use neutral or primary colors to avoid UX confusion.Example:
- Surface( - shape = RoundedCornerShape(12.dp), - color = MaterialTheme.colorScheme.errorContainer - ) { + Surface( + shape = RoundedCornerShape(12.dp), + color = MaterialTheme.colorScheme.primaryContainer + ) { @@ - color = MaterialTheme.colorScheme.onErrorContainer, + color = MaterialTheme.colorScheme.onPrimaryContainer,
443-474: ModelBadge duplication across package.There’s another
ModelBadgein ModelSelectionBottomSheet with a different API. Unify into one reusable composable to avoid subtle inconsistencies.
19-21: Remove unnecessary imports.
kotlin.collections.isNotEmptyis redundant; usemodels.isNotEmpty()without the import.-import kotlin.collections.isNotEmpty
60-87: Strings hardcoded in UI.Consider moving user-visible strings to resources for i18n and consistency.
Also applies to: 248-261, 264-441
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/data/ModelInfo.kt(1 hunks)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/repository/ModelRepository.kt(1 hunks)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/RunAnywhereApplication.kt(1 hunks)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/domain/models/SessionState.kt(1 hunks)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/domain/models/SpeakerInfo.kt(1 hunks)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/domain/services/AudioCaptureService.kt(1 hunks)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelSelectionBottomSheet.kt(1 hunks)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelSelectionViewModel.kt(1 hunks)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelsScreen.kt(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/data/ModelInfo.kt
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-10-07T00:42:41.855Z
Learnt from: CR
PR: RunanywhereAI/runanywhere-sdks#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-07T00:42:41.855Z
Learning: Applies to sdk/runanywhere-android/**/RunAnywhereSDK.kt : Expose the Android SDK main entry via RunAnywhereSDK.getInstance() (singleton)
Applied to files:
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/RunAnywhereApplication.kt
📚 Learning: 2025-10-07T00:42:41.855Z
Learnt from: CR
PR: RunanywhereAI/runanywhere-sdks#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-07T00:42:41.855Z
Learning: Applies to sdk/runanywhere-android/**/Configuration.kt : Provide configuration-based initialization for the Android SDK (API key and optional settings)
Applied to files:
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/RunAnywhereApplication.kt
📚 Learning: 2025-10-07T00:42:41.855Z
Learnt from: CR
PR: RunanywhereAI/runanywhere-sdks#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-07T00:42:41.855Z
Learning: Applies to sdk/runanywhere-android/**/*.kt : Use Kotlin coroutines for async APIs in the Android SDK
Applied to files:
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/RunAnywhereApplication.kt
📚 Learning: 2025-10-07T00:42:41.855Z
Learnt from: CR
PR: RunanywhereAI/runanywhere-sdks#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-07T00:42:41.855Z
Learning: Applies to sdk/runanywhere-swift/**/RunAnywhereSDK.swift : Expose the iOS SDK main entry via RunAnywhereSDK.shared (singleton)
Applied to files:
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/RunAnywhereApplication.kt
🧬 Code graph analysis (5)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelsScreen.kt (2)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelSelectionBottomSheet.kt (1)
ModelBadge(586-614)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/chat/ChatScreen.kt (1)
ModelBadge(524-569)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/repository/ModelRepository.kt (3)
sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/services/modelinfo/ModelInfoService.kt (1)
updateDownloadProgress(180-198)sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/models/ModelManager.kt (1)
calculateDirectorySize(212-235)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/data/repository/ModelRepositoryEnhanced.kt (1)
context(25-594)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelSelectionViewModel.kt (1)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelsViewModel.kt (1)
loadDeviceInfo(28-33)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelSelectionBottomSheet.kt (3)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelsScreen.kt (1)
ModelBadge(443-474)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/data/ModelInfo.kt (1)
formatBytes(60-67)examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelsViewModel.kt (1)
formatBytes(86-94)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/RunAnywhereApplication.kt (2)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/voice/TranscriptionViewModel.kt (1)
initializeSDK(39-55)sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/public/extensions/RunAnywhere+ModelManagement.kt (1)
addModelFromURL(161-225)
🪛 detekt (1.23.8)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/repository/ModelRepository.kt
[warning] 54-54: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 179-179: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
[warning] 182-182: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelSelectionBottomSheet.kt
[warning] 668-668: String.format("%.2f GB", gb) uses implicitly default locale for string formatting.
(detekt.potential-bugs.ImplicitDefaultLocale)
[warning] 671-671: String.format("%.0f MB", mb) uses implicitly default locale for string formatting.
(detekt.potential-bugs.ImplicitDefaultLocale)
🔇 Additional comments (5)
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/domain/models/SpeakerInfo.kt (3)
1-10: LGTM!The imports, type aliasing, and documentation are well-structured. Using a type alias for the SDK's
SpeakerInfocleanly avoids naming conflicts.
22-29: Verify the confidence default value.Defaulting
confidenceto1.0f(100% certainty) when the SDK returnsnullmight be misleading. A null confidence typically indicates "unknown," not "completely confident."Please verify how confidence is used in the UI. If it's displayed to users, consider:
- Using
0.0fto indicate low/unknown confidence- Handling null explicitly in the UI layer
- Adding a comment explaining why
1.0fis appropriate
34-51: LGTM!The deterministic color generation using
hashCode()ensures each speaker consistently receives the same color. The bitwise AND with0x7FFFFFFFproperly handles negative hash values.examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/RunAnywhereApplication.kt (1)
21-25: Past review issues have been addressed.Both initialization flags are now properly marked
@Volatile, andretryInitializationcorrectly useswithContextinstead ofGlobalScope.launch, ensuring proper suspension semantics. Well done!Also applies to: 249-253
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/domain/services/AudioCaptureService.kt (1)
77-91: LGTM: Audio processing methods are correct.Both
calculateRMS()andfloatsToBytes()are implemented correctly:
calculateRMS()properly converts little-endian PCM bytes to shorts and computes the RMS value for visualizationfloatsToBytes()correctly clamps and converts float samples to 16-bit PCM with appropriate byte orderingThe implementations are consistent with each other and follow best practices for audio processing.
Also applies to: 110-121
| init { | ||
| // Ensure models directory exists | ||
| if (!modelsDir.exists()) { | ||
| modelsDir.mkdirs() | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Verify directory creation success.
The mkdirs() call returns a boolean indicating whether the directory was successfully created, but the return value is not checked. If directory creation fails (due to permissions, insufficient storage, or other I/O errors), subsequent operations that rely on modelsDir will fail or behave unexpectedly.
Apply this diff to verify directory creation:
init {
// Ensure models directory exists
- if (!modelsDir.exists()) {
- modelsDir.mkdirs()
+ if (!modelsDir.exists() && !modelsDir.mkdirs()) {
+ throw IllegalStateException("Failed to create models directory at ${modelsDir.absolutePath}")
}
}📝 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.
| init { | |
| // Ensure models directory exists | |
| if (!modelsDir.exists()) { | |
| modelsDir.mkdirs() | |
| } | |
| } | |
| init { | |
| // Ensure models directory exists | |
| if (!modelsDir.exists() && !modelsDir.mkdirs()) { | |
| throw IllegalStateException("Failed to create models directory at ${modelsDir.absolutePath}") | |
| } | |
| } |
🤖 Prompt for AI Agents
In
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/repository/ModelRepository.kt
around lines 35-40, the init block calls modelsDir.mkdirs() but does not check
the boolean result; update the code to capture the return value and handle
failure: attempt to create directories, and if mkdirs() returns false and
modelsDir still does not exist, log an error (or use Android Log) and throw an
appropriate exception (e.g., IllegalStateException or IOException) so callers
fail fast and you avoid subsequent I/O errors; keep the existing
modelsDir.exists() check and guard against the race condition by verifying
existence after the mkdirs() call before proceeding.
...roid/RunAnywhereAI/app/src/main/java/com/runanywhere/ai/models/repository/ModelRepository.kt
Show resolved
Hide resolved
| fun startCapture(): Flow<ByteArray> { | ||
| // Convert VoiceAudioChunk to ByteArray for backward compatibility | ||
| return kotlinx.coroutines.flow.flow { | ||
| sdkAudioCapture.startContinuousCapture().collect { chunk -> | ||
| // Convert float samples back to 16-bit PCM bytes | ||
| val bytes = floatsToBytes(chunk.samples) | ||
| emit(bytes) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Verify behavior when startCapture() is called multiple times.
Each call to startCapture() creates a new flow that invokes sdkAudioCapture.startContinuousCapture() when collected. If multiple collections occur concurrently (e.g., multiple ViewModels or recompositions), the behavior is unclear—does the SDK start multiple capture sessions, throw an error, or silently replace the previous session?
Run this script to verify the SDK's behavior and identify usage patterns:
🏁 Script executed:
#!/bin/bash
# Description: Check SDK's AndroidAudioCapture implementation and usage patterns
# Search for AndroidAudioCapture implementation to understand concurrent capture behavior
ast-grep --pattern $'class AndroidAudioCapture {
$$$
fun startContinuousCapture() {
$$$
}
$$$
}'
# Find all usages of AudioCaptureService.startCapture to verify lifecycle management
rg -nP --type=kotlin -C3 'AudioCaptureService\s*\('
rg -nP --type=kotlin -C3 '\.startCapture\s*\(\s*\)'Length of output: 1001
🏁 Script executed:
#!/bin/bash
# Find where sdkAudioCapture is initialized/declared to understand SDK type
rg -nP --type=kotlin 'sdkAudioCapture' -B2 -A2
# Search for AndroidAudioCapture in all file types (may be Java)
find . -type f \( -name "*.kt" -o -name "*.java" \) -exec grep -l "AndroidAudioCapture" {} \;
# Find all .startCapture() call sites with full context
rg -nP '\\.startCapture\\s*\\(' -B5 -A5 --type=kotlin
# Search for Flow collection patterns to see if multiple concurrent collections are managed
rg -nP '\\.collect\\s*\\{' -B3 -A3 --type=kotlin | grep -A8 -B8 "startCapture\|AudioCapture"Length of output: 4287
🏁 Script executed:
#!/bin/bash
# Examine AndroidAudioCapture implementation
cat sdk/runanywhere-kotlin/src/androidMain/kotlin/com/runanywhere/sdk/audio/AndroidAudioCapture.kt
# Examine VoiceAssistantViewModel to see how startCapture() is used
cat ./examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/voice/VoiceAssistantViewModel.ktLength of output: 23566
Add guard to prevent concurrent audio captures.
The startContinuousCapture() API allows multiple flows to be created and collected independently, but the implementation shares mutable state (audioRecord, isRecording, sequenceNumber) across all flows. Multiple concurrent collections would cause race conditions on these shared instance variables.
While the current usage in VoiceAssistantViewModel creates only one flow, the API design invites misuse. Add a flag to guard against concurrent captures:
private var isCapturing = false
fun startContinuousCapture(): Flow<VoiceAudioChunk> = flow {
if (isCapturing) {
throw AudioCaptureException("Audio capture already in progress")
}
if (!hasRecordPermission()) {
throw AudioCaptureException("Microphone permission not granted")
}
isCapturing = true
try {
// ... existing capture logic ...
} finally {
isCapturing = false
}
}🤖 Prompt for AI Agents
In
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/domain/services/AudioCaptureService.kt
around lines 55-64, the startCapture/startContinuousCapture flow can be
collected concurrently and shares mutable state (audioRecord, isRecording,
sequenceNumber); add a guard boolean (e.g. private var isCapturing = false) that
checks and throws an AudioCaptureException if a capture is already in progress,
verify microphone permission before starting, set isCapturing = true before
entering the capture logic and clear it in a finally block (isCapturing = false)
so concurrent collectors are prevented and shared state is reset on exit.
| /** | ||
| * Get the current recording state | ||
| */ | ||
| fun isRecording(): Boolean = sdkAudioCapture.isAudioCaptureAvailable() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Semantic mismatch: isRecording() vs isAudioCaptureAvailable().
The method isRecording() suggests it returns whether audio is currently being captured, but it delegates to isAudioCaptureAvailable(), which sounds like it checks whether the audio capture capability is available on the device, not whether recording is active. This could lead to incorrect logic or UI state.
Verify the SDK's method semantics or rename this method to match its behavior:
If isAudioCaptureAvailable() indeed checks capability rather than active state, consider tracking recording state locally:
+ private var _isRecording = false
+
fun startCapture(): Flow<ByteArray> {
return kotlinx.coroutines.flow.flow {
+ _isRecording = true
sdkAudioCapture.startContinuousCapture().collect { chunk ->
val bytes = floatsToBytes(chunk.samples)
emit(bytes)
}
+ }.onCompletion {
+ _isRecording = false
}
}
fun stopCapture() {
sdkAudioCapture.stopCapture()
+ _isRecording = false
}
- fun isRecording(): Boolean = sdkAudioCapture.isAudioCaptureAvailable()
+ fun isRecording(): Boolean = _isRecording🏁 Script executed:
#!/bin/bash
# Description: Check SDK's isAudioCaptureAvailable() semantics and find proper active-recording check
# Find SDK implementation of isAudioCaptureAvailable
ast-grep --pattern $'class AndroidAudioCapture {
$$$
fun isAudioCaptureAvailable() {
$$$
}
$$$
}'
# Look for alternative methods that indicate active recording state
rg -nP --type=kotlin -C3 'fun\s+(isRecording|isCapturing|isActive)\s*\('Length of output: 2039
Now I need to examine the actual AudioCaptureService implementation to verify the current state and provide a definitive recommendation.
Let me proceed with checking the full file:
**Semantic mismatch: `isRecording()` returns device capability, not active recording state.**
The method `isRecording()` delegates to `sdkAudioCapture.isAudioCaptureAvailable()`, which checks whether the device supports audio capture, not whether recording is currently active. This semantic mismatch could cause incorrect UI state or logic errors—callers expecting `isRecording()` to return `true` during active capture will receive incorrect information.
Implement explicit state tracking:
```diff
+ private var _isRecording = false
+
fun startCapture(): Flow<ByteArray> {
return kotlinx.coroutines.flow.flow {
+ _isRecording = true
sdkAudioCapture.startContinuousCapture().collect { chunk ->
val bytes = floatsToBytes(chunk.samples)
emit(bytes)
}
+ }.onCompletion {
+ _isRecording = false
}
}
fun stopCapture() {
sdkAudioCapture.stopCapture()
+ _isRecording = false
}
- fun isRecording(): Boolean = sdkAudioCapture.isAudioCaptureAvailable()
+ fun isRecording(): Boolean = _isRecording
🏁 Script executed:
#!/bin/bash
# Examine full AudioCaptureService implementation
cat examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/domain/services/AudioCaptureService.ktLength of output: 149
---
🏁 Script executed:
```shell
#!/bin/bash
# Find AudioCaptureService.kt file
fd -t f "AudioCaptureService.kt" 2>/dev/null
# If found, show full content
fd -t f "AudioCaptureService.kt" -x cat {} \;
Length of output: 3772
Semantic mismatch confirmed: isRecording() returns device capability, not active recording state.
The method delegates to sdkAudioCapture.isAudioCaptureAvailable(), which indicates whether the device supports audio capture—not whether recording is currently active. Additionally, there is no state tracking mechanism, so isRecording() will always return the same value regardless of actual recording status. This causes a logic bug: callers cannot determine if capture is currently running.
Implement explicit state tracking:
+ private var _isRecording = false
+
fun startCapture(): Flow<ByteArray> {
return kotlinx.coroutines.flow.flow {
+ _isRecording = true
sdkAudioCapture.startContinuousCapture().collect { chunk ->
val bytes = floatsToBytes(chunk.samples)
emit(bytes)
}
+ }.onCompletion {
+ _isRecording = false
}
}
fun stopCapture() {
sdkAudioCapture.stopCapture()
+ _isRecording = false
}
- fun isRecording(): Boolean = sdkAudioCapture.isAudioCaptureAvailable()
+ fun isRecording(): Boolean = _isRecordingCommittable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/domain/services/AudioCaptureService.kt
around line 96, isRecording() incorrectly returns
sdkAudioCapture.isAudioCaptureAvailable() (device capability) rather than
current recording state; fix by adding an explicit boolean state field (e.g.,
private val isRecording = AtomicBoolean(false) or a volatile/synchronized
Boolean), initialize it to false, set it to true when capture starts and false
when capture stops (update all start/stop/cleanup code paths, including
error/exception branches), and change isRecording() to return that state; ensure
thread-safety when reading/updating the flag.
| onSelectModel = { | ||
| scope.launch { | ||
| viewModel.selectModel(model.id) | ||
| // Wait a bit to show success message | ||
| kotlinx.coroutines.delay(500) | ||
| onModelSelected(model) | ||
| onDismiss() | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sheet dismisses even when model load fails.
You always call onModelSelected and onDismiss after selectModel, regardless of outcome.
Update to use the proposed Boolean result:
- onSelectModel = {
+ onSelectModel = {
scope.launch {
- viewModel.selectModel(model.id)
- // Wait a bit to show success message
- kotlinx.coroutines.delay(500)
- onModelSelected(model)
- onDismiss()
+ val ok = viewModel.selectModel(model.id)
+ if (ok) {
+ // brief success affordance
+ kotlinx.coroutines.delay(500)
+ onModelSelected(model)
+ onDismiss()
+ }
}
}🤖 Prompt for AI Agents
In
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelSelectionBottomSheet.kt
around lines 162-170, the sheet always calls onModelSelected and onDismiss after
viewModel.selectModel(model.id) regardless of success; change the coroutine to
capture the Boolean result from selectModel, and only run the success path
(kotlinx.coroutines.delay(500), onModelSelected(model), onDismiss()) when the
result is true—if false, skip calling onModelSelected/onDismiss and instead exit
the coroutine (or optionally handle failure UI).
| fun downloadModel(modelId: String) { | ||
| viewModelScope.launch { | ||
| try { | ||
| android.util.Log.d("ModelSelectionVM", "⬇️ Starting download for model: $modelId") | ||
|
|
||
| _uiState.update { | ||
| it.copy( | ||
| selectedModelId = modelId, | ||
| isLoadingModel = true, | ||
| loadingProgress = "Starting download..." | ||
| ) | ||
| } | ||
|
|
||
| // Call SDK download API with progress | ||
| RunAnywhere.downloadModel(modelId).collect { progress -> | ||
| val progressPercent = (progress * 100).toInt() | ||
| android.util.Log.d("ModelSelectionVM", "📊 Download progress: $progressPercent%") | ||
|
|
||
| _uiState.update { | ||
| it.copy( | ||
| loadingProgress = "Downloading: $progressPercent%" | ||
| ) | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard against concurrent downloads (single-flight).
Starting another download while one is in progress can corrupt UI state and lead to racey progress updates.
Apply this diff to early-return when a model operation is active:
fun downloadModel(modelId: String) {
viewModelScope.launch {
try {
+ if (_uiState.value.isLoadingModel) {
+ android.util.Log.w("ModelSelectionVM", "Download ignored: another model op in progress")
+ return@launch
+ }
android.util.Log.d("ModelSelectionVM", "⬇️ Starting download for model: $modelId")📝 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.
| fun downloadModel(modelId: String) { | |
| viewModelScope.launch { | |
| try { | |
| android.util.Log.d("ModelSelectionVM", "⬇️ Starting download for model: $modelId") | |
| _uiState.update { | |
| it.copy( | |
| selectedModelId = modelId, | |
| isLoadingModel = true, | |
| loadingProgress = "Starting download..." | |
| ) | |
| } | |
| // Call SDK download API with progress | |
| RunAnywhere.downloadModel(modelId).collect { progress -> | |
| val progressPercent = (progress * 100).toInt() | |
| android.util.Log.d("ModelSelectionVM", "📊 Download progress: $progressPercent%") | |
| _uiState.update { | |
| it.copy( | |
| loadingProgress = "Downloading: $progressPercent%" | |
| ) | |
| } | |
| } | |
| fun downloadModel(modelId: String) { | |
| viewModelScope.launch { | |
| try { | |
| if (_uiState.value.isLoadingModel) { | |
| android.util.Log.w("ModelSelectionVM", "Download ignored: another model op in progress") | |
| return@launch | |
| } | |
| android.util.Log.d("ModelSelectionVM", "⬇️ Starting download for model: $modelId") | |
| _uiState.update { | |
| it.copy( | |
| selectedModelId = modelId, | |
| isLoadingModel = true, | |
| loadingProgress = "Starting download..." | |
| ) | |
| } | |
| // Call SDK download API with progress | |
| RunAnywhere.downloadModel(modelId).collect { progress -> | |
| val progressPercent = (progress * 100).toInt() | |
| android.util.Log.d("ModelSelectionVM", "📊 Download progress: $progressPercent%") | |
| _uiState.update { | |
| it.copy( | |
| loadingProgress = "Downloading: $progressPercent%" | |
| ) | |
| } | |
| } | |
| suspend fun selectModel(modelId: String) { | ||
| try { | ||
| android.util.Log.d("ModelSelectionVM", "🔄 Loading model into memory: $modelId") | ||
|
|
||
| _uiState.update { | ||
| it.copy( | ||
| selectedModelId = modelId, | ||
| isLoadingModel = true, | ||
| loadingProgress = "Loading model into memory..." | ||
| ) | ||
| } | ||
|
|
||
| // Call SDK to load model - matching iOS | ||
| RunAnywhere.loadModel(modelId) | ||
|
|
||
| android.util.Log.d("ModelSelectionVM", "✅ Model loaded successfully: $modelId") | ||
|
|
||
| // Get the loaded model from the updated models list | ||
| val loadedModel = _uiState.value.models.find { it.id == modelId } | ||
|
|
||
| _uiState.update { | ||
| it.copy( | ||
| loadingProgress = "Model loaded successfully!", | ||
| isLoadingModel = false, | ||
| selectedModelId = null, | ||
| currentModel = loadedModel // Track the currently loaded model | ||
| ) | ||
| } | ||
| } catch (e: Exception) { | ||
| android.util.Log.e("ModelSelectionVM", "❌ Failed to load model $modelId: ${e.message}", e) | ||
| _uiState.update { | ||
| it.copy( | ||
| isLoadingModel = false, | ||
| selectedModelId = null, | ||
| loadingProgress = "", | ||
| error = e.message ?: "Failed to load model" | ||
| ) | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don’t dismiss on load failure; return success from selectModel and refresh models after load.
selectModel updates UI but callers cannot know if load succeeded; UI proceeds to dismiss regardless. Also, currentModel is found from stale list (no refresh after load).
Apply this diff:
- suspend fun selectModel(modelId: String) {
+ suspend fun selectModel(modelId: String): Boolean {
try {
android.util.Log.d("ModelSelectionVM", "🔄 Loading model into memory: $modelId")
@@
- RunAnywhere.loadModel(modelId)
+ RunAnywhere.loadModel(modelId)
+ // Refresh list to reflect loaded state/current model accurately
+ loadModelsAndFrameworks()
@@
- _uiState.update {
+ _uiState.update {
it.copy(
loadingProgress = "Model loaded successfully!",
isLoadingModel = false,
selectedModelId = null,
- currentModel = loadedModel // Track the currently loaded model
+ currentModel = loadedModel // Track the currently loaded model
)
}
- } catch (e: Exception) {
+ return true
+ } catch (e: kotlinx.coroutines.CancellationException) {
+ throw e
+ } catch (e: Exception) {
android.util.Log.e("ModelSelectionVM", "❌ Failed to load model $modelId: ${e.message}", e)
_uiState.update {
it.copy(
isLoadingModel = false,
selectedModelId = null,
loadingProgress = "",
error = e.message ?: "Failed to load model"
)
}
+ return false
}
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/presentation/models/ModelSelectionViewModel.kt
around lines 185-224, change selectModel to return a Boolean indicating success,
refresh the models list after calling RunAnywhere.loadModel so the loadedModel
is retrieved from the updated list (not stale _uiState.models), and only clear
selectedModelId when the load actually succeeds; on failure keep selectedModelId
(so callers can detect failure and avoid dismiss) and return false. Concretely:
make the function suspend fun selectModel(modelId: String): Boolean, perform
RunAnywhere.loadModel inside try, call your existing reload/refresh method (or
fetch models from the source) after a successful load before finding
loadedModel, update uiState based on the refreshed list, set isLoadingModel
false and selectedModelId = null on success and return true, and in catch set
isLoadingModel false but leave selectedModelId intact, set error, and return
false.
...roid/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/RunAnywhereApplication.kt
Show resolved
Hide resolved
|
|
||
| } else { | ||
| // Production Mode - Real API key required | ||
| val apiKey = "testing_api_key" // TODO: Get from secure storage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove hard-coded API key before production.
The hard-coded "testing_api_key" is a security risk if committed to version control, even with a TODO comment. Use secure storage (e.g., Android Keystore, encrypted SharedPreferences) or environment-specific configuration to retrieve the API key at runtime.
- val apiKey = "testing_api_key" // TODO: Get from secure storage
+ val apiKey = getSecureApiKey() // Retrieve from secure storageAdd a helper method:
private fun getSecureApiKey(): String {
// Implement retrieval from Android Keystore or secure storage
throw NotImplementedError("Implement secure API key retrieval")
}🤖 Prompt for AI Agents
In
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/RunAnywhereApplication.kt
around line 73, the API key is hard-coded as "testing_api_key"; remove this
secret and replace it with a secure retrieval mechanism. Create a private helper
(e.g., getSecureApiKey()) that reads the key from Android Keystore-backed
storage or EncryptedSharedPreferences (or from BuildConfig/environment for
CI/dev), implement secure storage access and error handling there, and use that
helper to assign apiKey at runtime; ensure no secret literals remain in source
and update README or build scripts to document how to provision the key.
| // Llama 3.2 1B - good quality | ||
| addModelFromURL( | ||
| url = "https://huggingface.co/Qwen/Qwen2-0.5B-Instruct-GGUF/resolve/main/qwen2-0_5b-instruct-q4_0.gguf", | ||
| name = "QWEN 2 0.5B Q4_0 Instruct", | ||
| type = "LLM" | ||
| ) | ||
| Log.i("RunAnywhereApp", "✅ Registered: QWEN 2 0.5B Instruct Q6_K Q4_0") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix copy-paste errors in comments and logs.
Lines 177-183 contain inconsistencies:
- Line 177: Comment says "Llama 3.2 1B - good quality" but the model is actually "QWEN 2 0.5B Q4_0 Instruct"
- Line 183: Log message says "Q6_K Q4_0" but the model is only Q4_0 quantized
- // Llama 3.2 1B - good quality
+ // QWEN 2 0.5B - compact and efficient
addModelFromURL(
url = "https://huggingface.co/Qwen/Qwen2-0.5B-Instruct-GGUF/resolve/main/qwen2-0_5b-instruct-q4_0.gguf",
name = "QWEN 2 0.5B Q4_0 Instruct",
type = "LLM"
)
- Log.i("RunAnywhereApp", "✅ Registered: QWEN 2 0.5B Instruct Q6_K Q4_0")
+ Log.i("RunAnywhereApp", "✅ Registered: QWEN 2 0.5B Q4_0 Instruct")🤖 Prompt for AI Agents
In
examples/android/RunAnywhereAI/app/src/main/java/com/runanywhere/runanywhereai/RunAnywhereApplication.kt
around lines 177 to 183, fix the copy-paste mismatches: update the comment to
correctly describe the registered model as "QWEN 2 0.5B Q4_0 Instruct" (not
"Llama 3.2 1B"), and change the Log.i message to match the model name and
quantization (remove "Q6_K" so it logs "QWEN 2 0.5B Q4_0 Instruct" or similar
exact model string).
- Update Gradle wrapper to 8.13 - Synchronize versions to 0.1.0 across all modules - Add JitPack configuration for zero-auth consumption - Build system ready for alpha release
JitPack requires gradle-wrapper.jar to build the project. This file was being ignored despite gitignore negation rules.
Remove experimental JNI integration for Whisper and WebRTC VAD. These features will be implemented through platform-specific modules instead. Also remove SDK test action from IntelliJ plugin demo. Changes: - Removed sdk/runanywhere-kotlin/jni module (build.gradle.kts, CMakeLists.txt, NativeLoader.kt) - Removed SDKTestAction.kt from IntelliJ plugin demo - Updated plugin.xml to remove SDK test action reference 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this 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
♻️ Duplicate comments (2)
README.md (2)
9-9: Unresolved: Update badge to match body text on Android availability.The badge at line 9 still reads "Android SDK — Coming Soon" while the body text at lines 19-20 states the Android SDK is "Available." This creates conflicting status information for readers. Please update the badge to match the availability status declared in the body.
-[](sdk/runanywhere-android/) +[](sdk/runanywhere-kotlin/)Also applies to: 19-20
170-173: Unresolved: Add missingSDKEnvironmentimport to Android quick-start code.The Android code sample uses
SDKEnvironment.DEVELOPMENTat line 183 but the import statement is missing. Copy-pasting this snippet will fail to compile. Add the import alongside the existing imports to make the example runnable.import com.runanywhere.sdk.public.RunAnywhere import com.runanywhere.sdk.llm.llamacpp.LlamaCppModule import com.runanywhere.sdk.models.RunAnywhereGenerationOptions +import com.runanywhere.sdk.public.SDKEnvironmentAlso applies to: 183-183
🧹 Nitpick comments (2)
.gitignore (2)
81-127: Well-structured IDE configuration section with appropriate exceptions.The expanded IDE section clearly separates machine-specific configurations (gradle.xml, misc.xml) from shared team configurations (!.idea/runConfigurations/, !.idea/vcs.xml), which follows best practices. The granular coverage of user-specific, generated, and Android Studio–specific files is comprehensive.
Minor observations:
- Line 98 (
.idea/*.iml) is partially redundant given line 100 (*.iml), though the specific pattern is harmless.- Lines 125–126 hardcode nested module paths. Consider whether these need updating if additional modules are added to the project structure.
197-206: Verify MLC-LLM submodule artifact paths.Lines 197–202 specify detailed output artifact patterns for the mlc-llm submodule within the runanywhere-llm-mlc module. The exception for output/README.md (line 205) is appropriate for preserving documentation.
Confirm that these exact paths match the actual build output structure of the mlc-llm submodule.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
sdk/runanywhere-kotlin/gradle/wrapper/gradle-wrapper.jaris excluded by!**/*.jar
📒 Files selected for processing (2)
.gitignore(4 hunks)README.md(7 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-10-07T00:42:41.855Z
Learnt from: CR
PR: RunanywhereAI/runanywhere-sdks#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-07T00:42:41.855Z
Learning: Applies to sdk/runanywhere-android/**/build.gradle{,.kts} : Use Kotlin version 2.0.21 in the Android SDK
Applied to files:
README.md
📚 Learning: 2025-10-07T00:42:41.855Z
Learnt from: CR
PR: RunanywhereAI/runanywhere-sdks#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-07T00:42:41.855Z
Learning: Applies to sdk/runanywhere-android/**/*.kt : Use Kotlin coroutines for async APIs in the Android SDK
Applied to files:
README.md
📚 Learning: 2025-10-07T00:42:41.855Z
Learnt from: CR
PR: RunanywhereAI/runanywhere-sdks#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-07T00:42:41.855Z
Learning: Applies to sdk/runanywhere-android/**/RunAnywhereSDK.kt : Expose the Android SDK main entry via RunAnywhereSDK.getInstance() (singleton)
Applied to files:
README.md
📚 Learning: 2025-10-17T22:19:36.568Z
Learnt from: shubhammalhotra28
PR: RunanywhereAI/runanywhere-sdks#119
File: README.md:176-176
Timestamp: 2025-10-17T22:19:36.568Z
Learning: The RunAnywhere SDK package repository is located at `https://github.com/RunanywhereAI/runanywhere-sdks`. The repository was moved from `sdks` to `runanywhere-sdks`, so the correct URL to reference in documentation and installation instructions is `https://github.com/RunanywhereAI/runanywhere-sdks`.
Applied to files:
README.md
🪛 LanguageTool
README.md
[grammar] ~521-~521: Use a hyphen to join words.
Context: ...ject includes code from third-party open source projects. See [THIRD_PARTY_LICENS...
(QB_NEW_EN_HYPHEN)
🔇 Additional comments (5)
.gitignore (3)
216-226: Kotlin SDK configuration with good preservation of templates.The Kotlin SDK configuration section properly ignores environment-specific files (dev.json, staging.json, prod.json) while preserving template and example files (lines 224–226). This aligns with best practices for managing configuration across environments.
Verify that the config paths (lines 217–221) align with the actual SDK directory structure.
228-239: Native build artifact patterns are comprehensive but verify against actual build structure.Lines 228–239 cover native build artifacts for llama-jni (lines 229–231) and generic compiled objects/libraries (lines 235–239). The comment on line 233 states "llama.cpp is a submodule," but the paths reference llama-jni and mlc-llm specifically—ensure the comment accurately reflects the submodule structure and the patterns match actual build outputs.
192-195: Verify external repository and distribution directories.Lines 192–195 add top-level patterns for EXTERNAL/ and dist/. Confirm these directories exist or are expected to be created as part of the SDK structure.
README.md (2)
248-248: Verify Kotlin version requirement.Line 248 specifies "Kotlin: 2.1.21+" but prior learnings indicate the project should use Kotlin 2.0.21. Confirm whether 2.1.21 is intentional (e.g., due to IDE/build tool requirements) or if this should align with the established guideline.
49-51: Well-structured Android SDK documentation and installation guide.The Android SDK section is comprehensive and consistent with iOS. The Gradle/Maven/Local Maven installation options, system requirements, and code examples are clear and helpful. The Android quick-start structure mirrors iOS well, making it easy for developers familiar with one SDK to onboard to the other.
Also applies to: 293-354
| ### Third-Party Licenses | ||
|
|
||
| This project includes code from third-party open source projects. | ||
| See [THIRD_PARTY_LICENSES.md](THIRD_PARTY_LICENSES.md) for the complete list of third-party licenses | ||
| and acknowledgments, including: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix grammar: hyphenate "open-source".
Line 521 reads "third-party open source projects" but should be "third-party open-source projects" for grammatical correctness (compound adjective before noun requires hyphenation).
-This project includes code from third-party open source projects.
+This project includes code from third-party open-source projects.📝 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.
| ### Third-Party Licenses | |
| This project includes code from third-party open source projects. | |
| See [THIRD_PARTY_LICENSES.md](THIRD_PARTY_LICENSES.md) for the complete list of third-party licenses | |
| and acknowledgments, including: | |
| ### Third-Party Licenses | |
| This project includes code from third-party open-source projects. | |
| See [THIRD_PARTY_LICENSES.md](THIRD_PARTY_LICENSES.md) for the complete list of third-party licenses | |
| and acknowledgments, including: |
🧰 Tools
🪛 LanguageTool
[grammar] ~521-~521: Use a hyphen to join words.
Context: ...ject includes code from third-party open source projects. See [THIRD_PARTY_LICENS...
(QB_NEW_EN_HYPHEN)
🤖 Prompt for AI Agents
In README.md around lines 519 to 523, the phrase "third-party open source
projects" should use a hyphenated compound adjective; update the text to read
"third-party open-source projects" so the compound modifier before the noun is
grammatically correct.
… PersistentDeviceIdentity - Added command for creating GitHub issues in create_gh_issue.md. - Introduced comprehensive iOS SDK architecture reference document. - Implemented thread safety measures in ModuleRegistry and PersistentDeviceIdentity to prevent race conditions during concurrent access. - Updated CircuitBreakerRegistry for synchronized access to circuit breakers. These changes aim to improve the overall stability and maintainability of the SDK while providing clearer documentation for developers.
|
@coderabbitai please review the PR |
|
@sanchitmonga22, I'll conduct a comprehensive review of this PR. This is a substantial update introducing the Android + JVM SDK with Kotlin Multiplatform support, including:
I'll analyze the code quality, architecture, potential issues, and provide detailed feedback on all changes. ✅ Actions performedReview triggered.
|
- Introduced a comprehensive Quick Start Guide for creating a simple chat app using the RunAnywhere Android SDK. - Updated README to include necessary imports for SDK initialization. - Enhanced release guide with platform-scoped tagging instructions for iOS. - Improved error handling and cleanup in model download and initialization processes. - Refactored ModelRepository and UI components for better context management and user experience. These changes aim to streamline the onboarding process for developers and improve the overall stability of the SDK.
build.gradle.ktsto include Kotlin and IntelliJ plugin versions for improved compatibility.IMPLEMENTATION_SUMMARY.mdandREADME.mdto provide comprehensive documentation on the SDK's architecture, features, and usage.settings.gradle.kts, enhancing maintainability.WebRTCVADComponentandWhisperSTTComponent, ensuring a modular design.AnalyticsTrackerfor monitoring SDK usage and performance.Description
Brief description of the changes made.
Type of Change
Testing
Labels
Please add the appropriate label(s):
iOS SDK- Changes to iOS/Swift SDKAndroid SDK- Changes to Android/Kotlin SDKiOS Sample- Changes to iOS example appAndroid Sample- Changes to Android example appChecklist
Screenshots - Attach all the relevant UI changes screenshots for iOS/Android and MacOS/Tablet/large screen sizes
Summary by CodeRabbit
New Features
Documentation
Build & Tooling
Removed Features