-
Notifications
You must be signed in to change notification settings - Fork 101
Flutter rearch #251
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
base: smonga/react_native_rearch
Are you sure you want to change the base?
Flutter rearch #251
Conversation
- Introduced new capabilities for LLM, STT, TTS, and VAD, enhancing functionality and modularity. - Added quality report files for improved code quality tracking and analysis. - Removed deprecated analytics services and unnecessary files to streamline the SDK. - Updated various service and model files to improve organization and maintainability. - Implemented new network services and authentication mechanisms for better security and performance. - Enhanced logging and analytics infrastructure for improved monitoring and debugging.
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the 📝 WalkthroughWalkthroughComprehensive Flutter SDK restructuring aligning iOS parity: removes deprecated memory management, voice handlers, analytics, native FFI bindings, and component-based services (VLM, WakeWord, TTS, VAD); introduces capability-based architecture with managed lifecycles, resource types, and structured error handling; updates public APIs with new model artifact types and speaker diarization support; enhances native library loading with diagnostics; updates linting rules and documentation. Changes
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Areas requiring extra attention:
Possibly related PRs
Suggested labels
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 3 inconclusive)
✅ Passed checks (1 passed)
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 |
…rization and model management - Introduced PARITY_REPORT.json and PARITY_REPORT.md for comprehensive analysis of iOS SDK parity. - Added SpeakerInfo type and ModelArtifactType sealed class to enhance model management. - Updated RunAnywhere class with new methods for speaker diarization: updateSpeakerName and resetSpeakerDiarization. - Removed unnecessary RoutingService and related classes to align with iOS SDK structure. - Enhanced GenerationResult with additional fields for improved output validation and reasoning support. - Ensured overall API parity with iOS SDK, achieving 100% match on public APIs.
- Added `addModelFromURL` method to `RegistryService` for model registration via download URL. - Introduced filtering methods in `RegistryService` to retrieve models by framework and category. - Updated `ModelFormat` with a method to infer format from filename. - Implemented `DefaultAnalyticsService` for improved event tracking and batch processing. - Enhanced `StorageAnalyzer` and `StorageMonitoring` for comprehensive storage management and monitoring. - Removed deprecated wake word detection components to streamline the SDK. - Ensured overall API consistency and improved code organization across various modules.
- Updated native backend imports to streamline file structure and improve clarity. - Removed deprecated native backend files to clean up the codebase. - Introduced module discovery and registration methods for improved module management. - Enhanced error handling in SDKError to provide more informative messages. - Added network retry utility for robust handling of network operations. - Implemented analytics services for LLM, STT, and TTS to track performance metrics. - Created device registration models and services for improved device management.
- Updated import paths for native backend files to improve clarity and consistency. - Removed deprecated files and unnecessary imports to streamline the codebase. - Enhanced module registration and organization for better maintainability. - Cleaned up various model and protocol files to align with the latest SDK structure.
- Added 'dart:async' imports to various files for improved asynchronous handling. - Updated LLM and TTS service classes to include support for streaming and asynchronous cancellation methods. - Refactored TTS configuration and input models for better clarity and usability. - Cleaned up unused imports and ensured consistency across service implementations.
- Added 'path' dependency to pubspec.yaml for improved file handling. - Refactored RunanywherePlugin to include versioning and enhanced native library loading strategy. - Introduced VoiceSessionHandle for managing active voice sessions, including cancellation and transcription handling. - Updated voice session models to align with iOS structure, improving consistency and usability. - Enhanced logging and error handling in voice session management for better diagnostics.
- Refactored StreamingService to support metrics tracking and backward compatibility with streaming generation. - Introduced LLMStreamingResult and LLMGenerationResult classes to encapsulate streaming results and metrics. - Added analytics service integration for tracking generation and model loading events. - Updated STT and TTS capabilities to include analytics services for improved performance tracking. - Cleaned up imports and organized code structure for better maintainability.
|
@coderabbitai please do a detailed review |
|
@greptile please do a detailed review |
|
@sanchitmonga22: I'll perform a detailed review of all the changes in this PR, examining the new LLM, STT, TTS, and VAD capabilities, the removal of deprecated analytics services, service/model updates, new network services, authentication mechanisms, and the enhanced logging infrastructure. ✅ Actions performedReview triggered.
|
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.
Additional Comments (1)
-
sdk/runanywhere-flutter/lib/backends/onnx/services/onnx_tts_service.dart, line 56-62 (link)logic:
_isInitializedset totrueeven when model path is empty and no model is loadedIf
modelPathis null/empty and_backend.isTtsModelLoadedis false, initialization still succeeds (line 60). This creates an inconsistency where_isInitialized = truebutisReadyreturns false. Consider throwing an error if no model is provided and none is loaded.
293 files reviewed, 4 comments
| Future<void> loadStoredTokens() async { | ||
| final storedAccessToken = | ||
| await KeychainManager.shared.retrieve(_accessTokenKey); | ||
| if (storedAccessToken != null) { | ||
| _accessToken = storedAccessToken; | ||
| _logger.debug('Loaded stored access token from keychain'); | ||
| } | ||
|
|
||
| final storedRefreshToken = | ||
| await KeychainManager.shared.retrieve(_refreshTokenKey); | ||
| if (storedRefreshToken != null) { | ||
| _refreshToken = storedRefreshToken; | ||
| _logger.debug('Loaded stored refresh token from keychain'); | ||
| } |
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.
logic: loadStoredTokens() doesn't restore _tokenExpiresAt, so stored tokens will always appear expired
When tokens are loaded from storage, _tokenExpiresAt remains null, causing getAccessToken() to always attempt a refresh (line 107-110). Consider storing and restoring the expiration time, or at least document that stored tokens require immediate refresh.
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdk/runanywhere-flutter/lib/data/network/services/authentication_service.dart
Line: 158:171
Comment:
**logic:** `loadStoredTokens()` doesn't restore `_tokenExpiresAt`, so stored tokens will always appear expired
When tokens are loaded from storage, `_tokenExpiresAt` remains null, causing `getAccessToken()` to always attempt a refresh (line 107-110). Consider storing and restoring the expiration time, or at least document that stored tokens require immediate refresh.
How can I resolve this? If you propose a fix, please make it concise.| unawaited(generateInBackground()); | ||
|
|
||
| return controller.stream; |
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.
logic: background task continues if caller stops listening to stream
unawaited(generateInBackground()) continues executing even if the returned stream is cancelled. Consider passing a cancellation token or listening to stream subscription cancellation to stop the background task.
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdk/runanywhere-flutter/lib/capabilities/streaming/streaming_service.dart
Line: 101:103
Comment:
**logic:** background task continues if caller stops listening to stream
`unawaited(generateInBackground())` continues executing even if the returned stream is cancelled. Consider passing a cancellation token or listening to stream subscription cancellation to stop the background task.
How can I resolve this? If you propose a fix, please make it concise.| } | ||
|
|
||
| // Request mic permission | ||
| final hasPermission = await _requestPermissionCallback?.call() ?? true; |
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.
logic: ?? true default grants permission if callback is null
If _requestPermissionCallback is null, permission is automatically granted. This could bypass permission checks. Consider defaulting to false or throwing an error when the callback isn't set.
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdk/runanywhere-flutter/lib/capabilities/voice/models/voice_session_handle.dart
Line: 76:76
Comment:
**logic:** `?? true` default grants permission if callback is null
If `_requestPermissionCallback` is null, permission is automatically granted. This could bypass permission checks. Consider defaulting to `false` or throwing an error when the callback isn't set.
How can I resolve this? If you propose a fix, please make it concise.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: 19
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
sdk/runanywhere-flutter/lib/core/models/model/model_info_metadata.dart (1)
89-104: Equality operator excludes fields that should be compared.The
==operator compares only 4 of 10 fields (author, license, description, version), excluding tags, trainingDataset, baseModel, quantizationLevel, minOSVersion, and minMemory. This violates the Dart equality contract: if two objects are equal, their hash code should be equal, and you should override the hashCode getter when overriding the == operator. Two ModelInfoMetadata instances with identical author, license, description, and version but different tags or baseModel would incorrectly be considered equal.Since all fields are used in copyWith(), fromJson(), and toJson(), include all fields in equality:
@override bool operator ==(Object other) => identical(this, other) || other is ModelInfoMetadata && runtimeType == other.runtimeType && author == other.author && license == other.license && tags == other.tags && description == other.description && trainingDataset == other.trainingDataset && baseModel == other.baseModel && quantizationLevel == other.quantizationLevel && version == other.version && minOSVersion == other.minOSVersion && minMemory == other.minMemory; @override int get hashCode => author.hashCode ^ license.hashCode ^ tags.hashCode ^ description.hashCode ^ trainingDataset.hashCode ^ baseModel.hashCode ^ quantizationLevel.hashCode ^ version.hashCode ^ minOSVersion.hashCode ^ minMemory.hashCode;sdk/runanywhere-flutter/lib/capabilities/voice/voice_capability_service.dart (3)
107-134: Resource leak: agent not cleaned up after stream processing completes.The agent created in
processVoiceis added to_activeAgentsbut never removed when the stream completes or errors. Each call accumulates agents, causing unbounded memory growth.🔎 Proposed fix to clean up agent after processing
Stream<VoiceAgentEvent> processVoice({ required Stream<VoiceAudioChunk> audioStream, VADConfiguration? vadParams, STTConfiguration? sttParams, LLMConfiguration? llmParams, TTSConfiguration? ttsParams, }) async* { + VoiceAgentCapability? agent; try { // Create agent - final agent = await createVoiceAgent( + agent = await createVoiceAgent( vadParams: vadParams, sttParams: sttParams, llmParams: llmParams, ttsParams: ttsParams, ); // Convert audio chunks to Data stream final dataStream = audioStream.map((chunk) => chunk.data); // Process through agent await for (final event in agent.processStream(dataStream)) { yield event; } } catch (e) { yield VoiceAgentEvent.error(e); + } finally { + if (agent != null) { + await agent.cleanup(); + _activeAgents.removeWhere((_, v) => v == agent); + } } }
137-144: Active agent search ignoresmodelIdparameter.The loop returns the first STT service found without checking if it matches the requested
modelId. This can return an incorrect service when multiple agents with different models exist.🔎 Suggested fix to filter by modelId
// Check if any active agent has STT with the specified model for (final agent in _activeAgents.values) { final stt = agent.sttCapability?.getService(); - if (stt != null) { + if (stt != null && (modelId == null || agent.configuration.sttConfig.modelId == modelId)) { return stt; } }
167-177:modelIdparameter is unused.The method accepts
modelIdbut ignores it, returning the first LLM service found regardless of the model. This inconsistency with the method signature could lead to unexpected behavior.🔎 Suggested fix
Future<LLMService?> findLLMService({String? modelId}) async { // Check if any active agent has LLM with the specified model for (final agent in _activeAgents.values) { final llm = agent.llmCapability?.getService(); - if (llm != null) { + if (llm != null && (modelId == null || agent.configuration.llmConfig.modelId == modelId)) { return llm; } } return null; }sdk/runanywhere-flutter/lib/core/models/hardware/device_capabilities.dart (1)
165-179: Guard against division by zero in memory pressure calculation.The
memoryPressureLevelgetter performs division on Line 166 without checking iftotalMemoryis zero. SincefromJson(Line 189) defaultstotalMemoryto0when the JSON field is null or missing, this can lead to incorrect behavior:
- If
totalMemoryis0, the division producesNaN(for0/0) orInfinity(forpositive/0).- All subsequent comparisons fail, causing the method to incorrectly return
MemoryPressureLevel.low.- This could mislead resource allocation logic into attempting operations when memory data is invalid.
Consider adding a boundary check or returning a sentinel value (e.g.,
MemoryPressureLevel.unknownorMemoryPressureLevel.critical) whentotalMemoryis zero.🔎 Proposed fix
MemoryPressureLevel get memoryPressureLevel { + if (totalMemory <= 0) { + return MemoryPressureLevel.critical; + } final ratio = availableMemory / totalMemory; if (ratio < 0.1) { return MemoryPressureLevel.critical; } else if (ratio < 0.15) { return MemoryPressureLevel.warning; } else if (ratio < 0.2) { return MemoryPressureLevel.high; } else if (ratio < 0.4) { return MemoryPressureLevel.medium; } else { return MemoryPressureLevel.low; } }
♻️ Duplicate comments (1)
sdk/runanywhere-flutter/lib/capabilities/text_generation/generation_service.dart (1)
178-195: StructuredOutputValidation class is defined but never used.This class is declared to support structured output validation but is never instantiated or populated in the codebase. This is related to the incomplete implementation of the new GenerationResult fields.
This issue is directly related to the previous comment about unpopulated GenerationResult fields.
🧹 Nitpick comments (20)
sdk/runanywhere-flutter/lib/capabilities/voice/voice_capability_service.dart (1)
87-89: Consider using UUID for agent IDs to prevent collisions.Using
DateTime.now().millisecondsSinceEpoch.toString()for agent IDs can cause collisions ifcreateVoiceAgentis called multiple times within the same millisecond. Additionally, the generatedagentIdis not returned to the caller, preventing them from referencing or removing specific agents later.🔎 Suggested improvement
+import 'package:uuid/uuid.dart'; + +// In class: +static const _uuid = Uuid(); + // Track agent -final agentId = DateTime.now().millisecondsSinceEpoch.toString(); +final agentId = _uuid.v4(); _activeAgents[agentId] = agent;Alternatively, consider returning a tuple or wrapper containing both the agent and its ID so callers can manage the agent lifecycle.
sdk/runanywhere-flutter/QUALITY_REPORT.json (1)
66-91: Consider schema versioning and avoiding line-number references.
Line numbers in
filesarray (line 72) will become stale as code evolves. Consider referencing only filenames or function names, or regenerating this report automatically in CI.Schema version: Adding a
"schema_version": "1.0"field at the top level would help tooling handle format changes gracefully.The
unawaited()suggestion innext_actionsis good practice — it makes fire-and-forget intent explicit and satisfies lint rules.🔎 Suggested additions
{ + "schema_version": "1.0", "sdk_root": "sdk/runanywhere-flutter", "platform": "flutter",For the dynamic calls, consider referencing by symbol rather than line number:
- "files": ["onnx_adapter.dart:239", "allocation_manager.dart:152"] + "files": ["onnx_adapter.dart (OnnxAdapter.dynamicInvoke)", "allocation_manager.dart (AllocationManager.allocate)"]sdk/runanywhere-flutter/lib/core/capabilities/capability_protocols.dart (1)
251-282: Consider adding acopyWithmethod for immutable updates.
ModelLifecycleMetricsis immutable with many fields. If metrics are updated incrementally (e.g., incrementingtotalLoads), acopyWithmethod would reduce boilerplate. This is optional since usage patterns may vary.sdk/runanywhere-flutter/lib/backends/onnx/onnx_download_strategy.dart (1)
1-1: Verify dart:async usage.The
dart:asyncimport was added but may be unused in this file. Dart analyzer should flag this if unnecessary.sdk/runanywhere-flutter/lib/backends/onnx/providers/onnx_llm_provider.dart (1)
1-2: Consider removing unused import.The
dart:asyncimport appears unused sinceFutureis already available by default in Dart. Verify if this import is necessary.sdk/runanywhere-flutter/lib/backends/onnx/providers/onnx_tts_provider.dart (1)
1-2: Consider removing unused import.The
dart:asyncimport may be unnecessary sinceFutureis available by default. Verify actual usage before removing.sdk/runanywhere-flutter/lib/backends/onnx/providers/onnx_vad_provider.dart (1)
1-2: Consider removing unused import.The
dart:asyncimport appears to be unused. Dart analyzer will flag this if unnecessary.sdk/runanywhere-flutter/lib/backends/onnx/providers/onnx_stt_provider.dart (1)
1-2: Consider removing unused import.The
dart:asyncimport may be unnecessary unless explicitly required for async patterns beyond the defaultFutureavailability.sdk/runanywhere-flutter/lib/backends/onnx/services/onnx_tts_service.dart (1)
46-105: Good refactoring with minor enhancement opportunity.The initialization logic is solid, with proper error handling and graceful degradation when
modelPathis null. The configuration-based API is cleaner than the previous approach.Optional enhancement: Lines 87-92 use a hardcoded
'en-US'language for all voices. Consider parsing language information from voice IDs when available to improve internationalization support. This can be deferred as the current implementation is functional.sdk/runanywhere-flutter/lib/capabilities/streaming/streaming_service.dart (1)
109-187: Consider extracting shared streaming logic to reduce duplication.Both
generateStreamandgenerateStreamWithMetricsshare nearly identical setup logic (model validation, analytics service instantiation, metrics collector creation, streaming loop). This duplication increases maintenance burden.Consider extracting the common logic into a private helper that both methods can use, differentiated only by whether to complete a result future.
🔎 Suggested approach
// Private helper that both methods can use Future<LLMGenerationResult?> _executeStreaming({ required String prompt, required RunAnywhereGenerationOptions options, required StreamController<String> controller, Completer<LLMGenerationResult>? resultCompleter, }) async { final loadedModel = generationService.getCurrentModel(); if (loadedModel == null) { final error = SDKError.modelNotFound('No model is currently loaded'); controller.addError(error); resultCompleter?.completeError(error); await controller.close(); return null; } // ... rest of shared logic } // Then generateStream becomes: Stream<String> generateStream({...}) { final controller = StreamController<String>.broadcast(); unawaited(_executeStreaming( prompt: prompt, options: options, controller: controller, )); return controller.stream; }sdk/runanywhere-flutter/PARITY_REPORT.md (1)
470-473: Documentation looks comprehensive and well-structured.The parity report provides excellent visibility into the iOS/Flutter alignment work.
Minor markdown lint note: Line 472 uses emphasis (
**Overall Parity: 100%**) instead of a heading, which triggers MD036. Consider using## Overall Parity: 100%for stricter compliance, though the current approach is acceptable for a celebratory status line.sdk/runanywhere-flutter/QUALITY_REPORT.md (1)
75-80: Add language specification to fenced code block.The analyzer output code block is missing a language identifier. Consider adding
textorplaintextfor proper markdown rendering and linting compliance.Proposed fix
-``` +```text Analyzing lib... 2 warnings (avoid_dynamic_calls) 22 info (discarded_futures) No errors.</details> </blockquote></details> <details> <summary>sdk/runanywhere-flutter/lib/core/module/model_storage_strategy.dart (2)</summary><blockquote> `117-143`: **Inconsistent `primaryFile` selection.** `getModelStorageInfo` uses `files.first` for `primaryFile` (line 130), but the `size` comes from `detectModel` which returns the first file with a known format extension (line 103). These could be different files if the directory contains non-model files. Consider returning the same file that `detectModel` identified: <details> <summary>Proposed fix</summary> ```diff @override ModelStorageDetails? getModelStorageInfo(Directory modelFolder) { final modelInfo = detectModel(modelFolder); if (modelInfo == null) return null; final (format, size) = modelInfo; int fileCount = 0; String? primaryFile; try { final files = modelFolder.listSync().whereType<File>().toList(); fileCount = files.length; - if (files.isNotEmpty) { - primaryFile = files.first.path.split(Platform.pathSeparator).last; - } + // Find the actual model file that was detected + for (final file in files) { + final ext = file.path.split('.').last.toLowerCase(); + if (ModelFormat.fromRawValue(ext) == format) { + primaryFile = file.path.split(Platform.pathSeparator).last; + break; + } + } } catch (_) { // Ignore errors }
87-88: Consider logging exceptions instead of silent swallow.The
catch (_)blocks silently discard all exceptions, making it harder to diagnose issues in production. Consider logging at debug level before returning null.Example for one catch block
- } catch (_) { - return null; - } + } catch (e) { + // Log for debugging but don't propagate - graceful degradation + assert(() { print('DefaultModelStorageStrategy: $e'); return true; }()); + return null; + }Also applies to: 106-108, 132-134
sdk/runanywhere-flutter/lib/capabilities/voice/models/voice_session_handle.dart (2)
153-154: Extract magic number to a named constant.The value
16000representing ~0.5s of audio at 16kHz should be a named constant for clarity and maintainability.🔎 Proposed fix
+ /// Minimum audio samples required for processing (~0.5s at 16kHz) + static const int _minAudioSamples = 16000; + // In _checkSpeechState: - if (_audioBuffer.length > 16000) { + if (_audioBuffer.length > _minAudioSamples) {
116-127: Consider usingBytesBuilderfor more efficient buffer accumulation.Creating a new
Uint8Liston everyfeedAudio()call is inefficient for high-frequency audio data.BytesBuilderprovides better performance for incremental byte accumulation.🔎 Proposed fix
+import 'dart:typed_data' show BytesBuilder; + - Uint8List _audioBuffer = Uint8List(0); + final BytesBuilder _audioBufferBuilder = BytesBuilder(copy: false); void feedAudio(Uint8List data, double audioLevel) { if (!_isRunning) return; - // Append to buffer - final newBuffer = Uint8List(_audioBuffer.length + data.length); - newBuffer.setRange(0, _audioBuffer.length, _audioBuffer); - newBuffer.setRange(_audioBuffer.length, newBuffer.length, data); - _audioBuffer = newBuffer; + _audioBufferBuilder.add(data); // Check speech state _checkSpeechState(audioLevel); } + + // When consuming buffer: + Uint8List _takeBuffer() { + final bytes = _audioBufferBuilder.takeBytes(); + return bytes; + }sdk/runanywhere-flutter/lib/capabilities/voice/models/voice_session.dart (2)
120-128: Consider including error type intoString().The current
toString()discards theVoiceSessionErrorType, which is useful for debugging and logging.🔎 Proposed fix
@override - String toString() => message; + String toString() => 'VoiceSessionException(${type.name}): $message';
166-169: Mutable state in a class withcopyWithpattern.The class mixes mutable (
state) and final-but-internally-mutable (transcripts) fields with an immutability-suggestingcopyWithmethod. If this is intentional for internal tracking (as the doc comment suggests), consider makingtranscriptsunmodifiable when exposed, or documenting the mutation expectations clearly.🔎 Option: Return unmodifiable view or make state final
/// Transcripts collected during this session - final List<STTOutput> transcripts; + final List<STTOutput> _transcripts; + + /// Transcripts collected during this session (unmodifiable view) + List<STTOutput> get transcripts => List.unmodifiable(_transcripts); + + /// Add a transcript (internal use) + void addTranscript(STTOutput transcript) => _transcripts.add(transcript);Or, if full immutability is desired, make
statefinal and usecopyWithexclusively.sdk/runanywhere-flutter/android/src/main/kotlin/ai/runanywhere/flutter/RunanywherePlugin.kt (1)
35-38: Consider thread-safety for companion object mutable state.The
nativeLibrariesLoaded,nativeLoadError, andloadedLibrariesare mutable and accessed without synchronization. While Flutter plugin attachment typically runs on the main thread, multi-engine scenarios or explicit calls from background threads could cause race conditions.🔎 Optional: Add synchronization for robustness
// Track if native libraries are loaded - private var nativeLibrariesLoaded = false - private var nativeLoadError: String? = null - private val loadedLibraries = mutableListOf<String>() + @Volatile private var nativeLibrariesLoaded = false + @Volatile private var nativeLoadError: String? = null + private val loadedLibraries = mutableListOf<String>() + private val loadLock = Any()Then wrap the loading logic in
loadNativeLibraries()withsynchronized(loadLock) { ... }.sdk/runanywhere-flutter/lib/core/module/inference_framework.dart (1)
39-46: Consider usingfirstWherefor more idiomatic Dart.The current implementation is correct, but you could make it more concise using Dart's collection methods.
🔎 Proposed refactor using firstWhere
- static InferenceFramework? fromRawValue(String value) { - for (final framework in InferenceFramework.values) { - if (framework.rawValue == value) { - return framework; - } - } - return null; - } + static InferenceFramework? fromRawValue(String value) { + return InferenceFramework.values + .cast<InferenceFramework?>() + .firstWhere( + (framework) => framework?.rawValue == value, + orElse: () => null, + ); + }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (107)
sdk/runanywhere-flutter/PARITY_REPORT.json(1 hunks)sdk/runanywhere-flutter/PARITY_REPORT.md(1 hunks)sdk/runanywhere-flutter/QUALITY_REPORT.json(1 hunks)sdk/runanywhere-flutter/QUALITY_REPORT.md(1 hunks)sdk/runanywhere-flutter/analysis_options.yaml(1 hunks)sdk/runanywhere-flutter/android/src/main/kotlin/ai/runanywhere/flutter/RunanywherePlugin.kt(1 hunks)sdk/runanywhere-flutter/lib/backends/backends.dart(1 hunks)sdk/runanywhere-flutter/lib/backends/llamacpp/llamacpp_adapter.dart(1 hunks)sdk/runanywhere-flutter/lib/backends/llamacpp/llamacpp_backend.dart(1 hunks)sdk/runanywhere-flutter/lib/backends/llamacpp/providers/llamacpp_llm_provider.dart(1 hunks)sdk/runanywhere-flutter/lib/backends/llamacpp/services/llamacpp_llm_service.dart(5 hunks)sdk/runanywhere-flutter/lib/backends/native/ffi_types.dart(0 hunks)sdk/runanywhere-flutter/lib/backends/native/native.dart(0 hunks)sdk/runanywhere-flutter/lib/backends/native/native_backend.dart(0 hunks)sdk/runanywhere-flutter/lib/backends/native/platform_loader.dart(0 hunks)sdk/runanywhere-flutter/lib/backends/onnx/onnx_adapter.dart(3 hunks)sdk/runanywhere-flutter/lib/backends/onnx/onnx_backend.dart(1 hunks)sdk/runanywhere-flutter/lib/backends/onnx/onnx_download_strategy.dart(4 hunks)sdk/runanywhere-flutter/lib/backends/onnx/providers/onnx_llm_provider.dart(1 hunks)sdk/runanywhere-flutter/lib/backends/onnx/providers/onnx_stt_provider.dart(1 hunks)sdk/runanywhere-flutter/lib/backends/onnx/providers/onnx_tts_provider.dart(1 hunks)sdk/runanywhere-flutter/lib/backends/onnx/providers/onnx_vad_provider.dart(1 hunks)sdk/runanywhere-flutter/lib/backends/onnx/services/onnx_llm_service.dart(2 hunks)sdk/runanywhere-flutter/lib/backends/onnx/services/onnx_stt_service.dart(1 hunks)sdk/runanywhere-flutter/lib/backends/onnx/services/onnx_tts_service.dart(6 hunks)sdk/runanywhere-flutter/lib/backends/onnx/services/onnx_vad_service.dart(1 hunks)sdk/runanywhere-flutter/lib/capabilities/analytics/generation/generation_analytics_service.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/analytics/stt/stt_analytics_service.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/analytics/voice/voice_analytics_service.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/download/download_service.dart(3 hunks)sdk/runanywhere-flutter/lib/capabilities/memory/allocation_manager.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/memory/cache_eviction.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/memory/memory_monitor.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/memory/memory_service.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/memory/models/memory_models.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/memory/monitors/threshold_watcher.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/memory/pressure_handler.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/model_loading/model_loading_service.dart(4 hunks)sdk/runanywhere-flutter/lib/capabilities/registry/registry_service.dart(1 hunks)sdk/runanywhere-flutter/lib/capabilities/routing/routing_service.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/streaming/streaming_service.dart(2 hunks)sdk/runanywhere-flutter/lib/capabilities/structured_output/structured_output_service.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/text_generation/generation_service.dart(5 hunks)sdk/runanywhere-flutter/lib/capabilities/voice/audio_pipeline_state.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/voice/handlers/llm_handler.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/voice/handlers/speaker_diarization_handler.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/voice/handlers/streaming_tts_handler.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/voice/handlers/stt_handler.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/voice/handlers/tts_handler.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/voice/handlers/vad_handler.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/voice/models/modular_pipeline_event.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/voice/models/voice_session.dart(2 hunks)sdk/runanywhere-flutter/lib/capabilities/voice/models/voice_session_handle.dart(1 hunks)sdk/runanywhere-flutter/lib/capabilities/voice/services/default_speaker_diarization.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/voice/services/voice_session_manager.dart(1 hunks)sdk/runanywhere-flutter/lib/capabilities/voice/strategies/audio_segmentation_strategy.dart(0 hunks)sdk/runanywhere-flutter/lib/capabilities/voice/voice_capability_service.dart(9 hunks)sdk/runanywhere-flutter/lib/components/speaker_diarization/speaker_diarization_component.dart(0 hunks)sdk/runanywhere-flutter/lib/components/tts/system_tts_service.dart(0 hunks)sdk/runanywhere-flutter/lib/components/tts/tts_component.dart(0 hunks)sdk/runanywhere-flutter/lib/components/tts/tts_options.dart(0 hunks)sdk/runanywhere-flutter/lib/components/tts/tts_service.dart(0 hunks)sdk/runanywhere-flutter/lib/components/vad/vad_output.dart(0 hunks)sdk/runanywhere-flutter/lib/components/vad/vad_service_provider.dart(0 hunks)sdk/runanywhere-flutter/lib/components/vlm/vlm_component.dart(0 hunks)sdk/runanywhere-flutter/lib/components/wake_word/wake_word_component.dart(0 hunks)sdk/runanywhere-flutter/lib/core/capabilities/capability_protocols.dart(1 hunks)sdk/runanywhere-flutter/lib/core/capabilities/managed_lifecycle.dart(1 hunks)sdk/runanywhere-flutter/lib/core/capabilities_base/base_capability.dart(3 hunks)sdk/runanywhere-flutter/lib/core/model_lifecycle_manager.dart(9 hunks)sdk/runanywhere-flutter/lib/core/models/common/common.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/common/lifecycle_stage.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/common/request_priority.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/common/resource_availability.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/configuration/api_configuration.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/configuration/cache_eviction_policy.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/configuration/configuration.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/configuration/configuration_data.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/configuration/download_policy.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/configuration/generation_configuration.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/configuration/model_download_configuration.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/configuration/privacy_mode.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/configuration/routing_configuration.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/configuration/routing_policy.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/configuration/storage_configuration.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/framework/framework.dart(1 hunks)sdk/runanywhere-flutter/lib/core/models/framework/framework_modality.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/framework/llm_framework.dart(1 hunks)sdk/runanywhere-flutter/lib/core/models/framework/model_artifact_type.dart(1 hunks)sdk/runanywhere-flutter/lib/core/models/framework/model_format.dart(1 hunks)sdk/runanywhere-flutter/lib/core/models/hardware/device_capabilities.dart(1 hunks)sdk/runanywhere-flutter/lib/core/models/model/model_category.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/model/model_info.dart(2 hunks)sdk/runanywhere-flutter/lib/core/models/model/model_info_metadata.dart(1 hunks)sdk/runanywhere-flutter/lib/core/models/model/model_registration.dart(1 hunks)sdk/runanywhere-flutter/lib/core/models/models.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/storage/storage.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/storage/storage_availability.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/storage/storage_recommendation.dart(1 hunks)sdk/runanywhere-flutter/lib/core/models/voice/noise_suppression_level.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/voice/voice.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/voice/voice_audio_chunk.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/voice/wake_word_config.dart(0 hunks)sdk/runanywhere-flutter/lib/core/models/voice/wake_word_detection.dart(0 hunks)sdk/runanywhere-flutter/lib/core/module/capability_type.dart(1 hunks)sdk/runanywhere-flutter/lib/core/module/inference_framework.dart(1 hunks)sdk/runanywhere-flutter/lib/core/module/model_storage_strategy.dart(1 hunks)
⛔ Files not processed due to max files limit (58)
- sdk/runanywhere-flutter/lib/core/module/module.dart
- sdk/runanywhere-flutter/lib/core/module/runanywhere_module.dart
- sdk/runanywhere-flutter/lib/core/module_registry.dart
- sdk/runanywhere-flutter/lib/core/protocols/analytics/analytics_service.dart
- sdk/runanywhere-flutter/lib/core/protocols/analytics/session_metadata.dart
- sdk/runanywhere-flutter/lib/core/protocols/component/component.dart
- sdk/runanywhere-flutter/lib/core/protocols/configuration/configuration.dart
- sdk/runanywhere-flutter/lib/core/protocols/configuration/configuration_service_protocol.dart
- sdk/runanywhere-flutter/lib/core/protocols/downloading/download_progress.dart
- sdk/runanywhere-flutter/lib/core/protocols/frameworks/unified_framework_adapter.dart
- sdk/runanywhere-flutter/lib/core/protocols/hardware/hardware.dart
- sdk/runanywhere-flutter/lib/core/protocols/hardware/hardware_detector.dart
- sdk/runanywhere-flutter/lib/core/protocols/lifecycle/lifecycle.dart
- sdk/runanywhere-flutter/lib/core/protocols/lifecycle/model_lifecycle_progress_observer.dart
- sdk/runanywhere-flutter/lib/core/protocols/lifecycle/model_lifecycle_protocol.dart
- sdk/runanywhere-flutter/lib/core/protocols/memory/memory.dart
- sdk/runanywhere-flutter/lib/core/protocols/memory/memory_manager.dart
- sdk/runanywhere-flutter/lib/core/protocols/protocols.dart
- sdk/runanywhere-flutter/lib/core/protocols/storage/storage.dart
- sdk/runanywhere-flutter/lib/core/protocols/storage/storage_analyzer.dart
- sdk/runanywhere-flutter/lib/core/protocols/storage/storage_monitoring.dart
- sdk/runanywhere-flutter/lib/core/protocols/voice/voice.dart
- sdk/runanywhere-flutter/lib/core/protocols/voice/wake_word_detector.dart
- sdk/runanywhere-flutter/lib/core/service_registry/adapter_selection_strategy.dart
- sdk/runanywhere-flutter/lib/core/service_registry/unified_service_registry.dart
- sdk/runanywhere-flutter/lib/core/types/sdk_component.dart
- sdk/runanywhere-flutter/lib/data/data.dart
- sdk/runanywhere-flutter/lib/data/errors/repository_error.dart
- sdk/runanywhere-flutter/lib/data/models/device_info_data.dart
- sdk/runanywhere-flutter/lib/data/models/models.dart
- sdk/runanywhere-flutter/lib/data/models/telemetry_data.dart
- sdk/runanywhere-flutter/lib/data/network/api_client.dart
- sdk/runanywhere-flutter/lib/data/network/api_endpoint.dart
- sdk/runanywhere-flutter/lib/data/network/development_network_config.dart
- sdk/runanywhere-flutter/lib/data/network/models/auth/auth.dart
- sdk/runanywhere-flutter/lib/data/network/models/auth/authentication_request.dart
- sdk/runanywhere-flutter/lib/data/network/models/auth/authentication_response.dart
- sdk/runanywhere-flutter/lib/data/network/models/auth/health_check_response.dart
- sdk/runanywhere-flutter/lib/data/network/models/auth/refresh_token_request.dart
- sdk/runanywhere-flutter/lib/data/network/network.dart
- sdk/runanywhere-flutter/lib/data/network/network_service.dart
- sdk/runanywhere-flutter/lib/data/network/services/authentication_service.dart
- sdk/runanywhere-flutter/lib/data/network/services/services.dart
- sdk/runanywhere-flutter/lib/data/protocols/configuration_repository.dart
- sdk/runanywhere-flutter/lib/data/protocols/data_source.dart
- sdk/runanywhere-flutter/lib/data/protocols/data_source_storage_info.dart
- sdk/runanywhere-flutter/lib/data/protocols/device_info_repository.dart
- sdk/runanywhere-flutter/lib/data/protocols/model_info_repository.dart
- sdk/runanywhere-flutter/lib/data/protocols/protocols.dart
- sdk/runanywhere-flutter/lib/data/protocols/remote_data_source.dart
- sdk/runanywhere-flutter/lib/data/protocols/repository.dart
- sdk/runanywhere-flutter/lib/data/protocols/repository_entity.dart
- sdk/runanywhere-flutter/lib/data/protocols/telemetry_repository.dart
- sdk/runanywhere-flutter/lib/data/sync/models/models.dart
- sdk/runanywhere-flutter/lib/data/sync/models/sync_progress.dart
- sdk/runanywhere-flutter/lib/data/sync/models/sync_state.dart
- sdk/runanywhere-flutter/lib/data/sync/sync.dart
- sdk/runanywhere-flutter/lib/data/sync/sync_coordinator.dart
💤 Files with no reviewable changes (60)
- sdk/runanywhere-flutter/lib/core/models/framework/framework_modality.dart
- sdk/runanywhere-flutter/lib/core/models/storage/storage_availability.dart
- sdk/runanywhere-flutter/lib/core/models/voice/voice_audio_chunk.dart
- sdk/runanywhere-flutter/lib/core/models/common/request_priority.dart
- sdk/runanywhere-flutter/lib/core/models/voice/noise_suppression_level.dart
- sdk/runanywhere-flutter/lib/core/models/configuration/storage_configuration.dart
- sdk/runanywhere-flutter/lib/core/models/configuration/routing_configuration.dart
- sdk/runanywhere-flutter/lib/core/models/configuration/routing_policy.dart
- sdk/runanywhere-flutter/lib/components/tts/system_tts_service.dart
- sdk/runanywhere-flutter/lib/core/models/common/common.dart
- sdk/runanywhere-flutter/lib/core/models/voice/voice.dart
- sdk/runanywhere-flutter/lib/core/models/model/model_category.dart
- sdk/runanywhere-flutter/lib/capabilities/voice/handlers/streaming_tts_handler.dart
- sdk/runanywhere-flutter/lib/capabilities/memory/memory_monitor.dart
- sdk/runanywhere-flutter/lib/components/tts/tts_options.dart
- sdk/runanywhere-flutter/lib/capabilities/memory/memory_service.dart
- sdk/runanywhere-flutter/lib/core/models/configuration/privacy_mode.dart
- sdk/runanywhere-flutter/lib/capabilities/memory/pressure_handler.dart
- sdk/runanywhere-flutter/lib/capabilities/structured_output/structured_output_service.dart
- sdk/runanywhere-flutter/lib/capabilities/memory/cache_eviction.dart
- sdk/runanywhere-flutter/lib/capabilities/routing/routing_service.dart
- sdk/runanywhere-flutter/lib/core/models/common/lifecycle_stage.dart
- sdk/runanywhere-flutter/lib/backends/native/native_backend.dart
- sdk/runanywhere-flutter/lib/capabilities/voice/models/modular_pipeline_event.dart
- sdk/runanywhere-flutter/lib/core/models/models.dart
- sdk/runanywhere-flutter/lib/core/models/storage/storage.dart
- sdk/runanywhere-flutter/lib/core/models/voice/wake_word_detection.dart
- sdk/runanywhere-flutter/lib/core/models/configuration/generation_configuration.dart
- sdk/runanywhere-flutter/lib/capabilities/memory/monitors/threshold_watcher.dart
- sdk/runanywhere-flutter/lib/backends/native/native.dart
- sdk/runanywhere-flutter/lib/core/models/common/resource_availability.dart
- sdk/runanywhere-flutter/lib/core/models/configuration/configuration_data.dart
- sdk/runanywhere-flutter/lib/capabilities/voice/handlers/tts_handler.dart
- sdk/runanywhere-flutter/lib/capabilities/analytics/voice/voice_analytics_service.dart
- sdk/runanywhere-flutter/lib/core/models/configuration/model_download_configuration.dart
- sdk/runanywhere-flutter/lib/capabilities/voice/handlers/vad_handler.dart
- sdk/runanywhere-flutter/lib/core/models/configuration/cache_eviction_policy.dart
- sdk/runanywhere-flutter/lib/components/tts/tts_service.dart
- sdk/runanywhere-flutter/lib/capabilities/analytics/stt/stt_analytics_service.dart
- sdk/runanywhere-flutter/lib/components/tts/tts_component.dart
- sdk/runanywhere-flutter/lib/components/vad/vad_output.dart
- sdk/runanywhere-flutter/lib/core/models/configuration/configuration.dart
- sdk/runanywhere-flutter/lib/capabilities/memory/allocation_manager.dart
- sdk/runanywhere-flutter/lib/capabilities/voice/handlers/llm_handler.dart
- sdk/runanywhere-flutter/lib/capabilities/analytics/generation/generation_analytics_service.dart
- sdk/runanywhere-flutter/lib/backends/native/platform_loader.dart
- sdk/runanywhere-flutter/lib/core/models/voice/wake_word_config.dart
- sdk/runanywhere-flutter/lib/capabilities/voice/audio_pipeline_state.dart
- sdk/runanywhere-flutter/lib/capabilities/voice/handlers/speaker_diarization_handler.dart
- sdk/runanywhere-flutter/lib/capabilities/memory/models/memory_models.dart
- sdk/runanywhere-flutter/lib/capabilities/voice/services/default_speaker_diarization.dart
- sdk/runanywhere-flutter/lib/components/wake_word/wake_word_component.dart
- sdk/runanywhere-flutter/lib/capabilities/voice/strategies/audio_segmentation_strategy.dart
- sdk/runanywhere-flutter/lib/components/vad/vad_service_provider.dart
- sdk/runanywhere-flutter/lib/core/models/configuration/download_policy.dart
- sdk/runanywhere-flutter/lib/capabilities/voice/handlers/stt_handler.dart
- sdk/runanywhere-flutter/lib/components/speaker_diarization/speaker_diarization_component.dart
- sdk/runanywhere-flutter/lib/components/vlm/vlm_component.dart
- sdk/runanywhere-flutter/lib/core/models/configuration/api_configuration.dart
- sdk/runanywhere-flutter/lib/backends/native/ffi_types.dart
🧰 Additional context used
🧠 Learnings (5)
📚 Learning: 2025-11-26T22:49:40.358Z
Learnt from: CR
Repo: RunanywhereAI/runanywhere-sdks PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T22:49:40.358Z
Learning: Applies to sdk/runanywhere-kotlin/**/src/**/*.kt : Use lazy initialization for service dependencies to avoid memory pressure and allow garbage collection.
Applied to files:
sdk/runanywhere-flutter/lib/capabilities/model_loading/model_loading_service.dart
📚 Learning: 2025-11-26T22:49:40.358Z
Learnt from: CR
Repo: RunanywhereAI/runanywhere-sdks PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T22:49:40.358Z
Learning: Applies to sdk/runanywhere-kotlin/**/src/**/components/**/*.kt : Implement proper component lifecycle management with initialization, state tracking, and cleanup in BaseComponent.
Applied to files:
sdk/runanywhere-flutter/lib/core/capabilities_base/base_capability.dart
📚 Learning: 2025-11-26T22:49:40.358Z
Learnt from: CR
Repo: RunanywhereAI/runanywhere-sdks PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T22:49:40.358Z
Learning: Applies to sdk/runanywhere-kotlin/**/src/**/*.kt : Use the `ModuleRegistry` provider pattern for extensibility and plugin architecture instead of hard-coded dependencies.
Applied to files:
sdk/runanywhere-flutter/android/src/main/kotlin/ai/runanywhere/flutter/RunanywherePlugin.kt
📚 Learning: 2025-11-26T22:49:40.358Z
Learnt from: CR
Repo: RunanywhereAI/runanywhere-sdks PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T22:49:40.358Z
Learning: Applies to sdk/runanywhere-kotlin/**/src/androidMain/**/*.kt : Use platform-specific optimizations in their respective source directories: `androidMain/` for Android optimizations, `jvmMain/` for JVM optimizations.
Applied to files:
sdk/runanywhere-flutter/android/src/main/kotlin/ai/runanywhere/flutter/RunanywherePlugin.kt
📚 Learning: 2025-11-26T22:49:40.358Z
Learnt from: CR
Repo: RunanywhereAI/runanywhere-sdks PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-26T22:49:40.358Z
Learning: Applies to sdk/runanywhere-kotlin/**/src/jvmMain/**/*.kt : Use platform-specific optimizations in their respective source directories: `androidMain/` for Android optimizations, `jvmMain/` for JVM optimizations.
Applied to files:
sdk/runanywhere-flutter/android/src/main/kotlin/ai/runanywhere/flutter/RunanywherePlugin.kt
🪛 detekt (1.23.8)
sdk/runanywhere-flutter/android/src/main/kotlin/ai/runanywhere/flutter/RunanywherePlugin.kt
[warning] 131-131: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
🪛 LanguageTool
sdk/runanywhere-flutter/PARITY_REPORT.md
[uncategorized] ~397-~397: If this is a compound adjective that modifies the following noun, use a hyphen.
Context: ...luding updateSpeakerName and reset ### Low Priority Cleanup 1. LLMFramework naming - C...
(EN_COMPOUND_ADJECTIVE_INTERNAL)
🪛 markdownlint-cli2 (0.18.1)
sdk/runanywhere-flutter/PARITY_REPORT.md
472-472: Emphasis used instead of a heading
(MD036, no-emphasis-as-heading)
sdk/runanywhere-flutter/QUALITY_REPORT.md
75-75: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Greptile Review
| } catch (e: UnsatisfiedLinkError) { | ||
| Log.e(TAG, " ✗ Failed to load required library: $name", e) | ||
| throw UnsatisfiedLinkError( | ||
| "Required library '$name' could not be loaded. " + | ||
| "Error: ${e.message}\n\n" + | ||
| "This may indicate:\n" + | ||
| " 1. Missing .so file in jniLibs/\n" + | ||
| " 2. Incorrect ABI architecture\n" + | ||
| " 3. Missing dependencies\n\n" + | ||
| "Successfully loaded: ${loadedLibraries.joinToString(", ")}" | ||
| ) | ||
| } |
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.
Preserve original exception cause for better diagnostics.
The static analysis correctly flags that the original exception's stack trace is lost when throwing the new UnsatisfiedLinkError. Chain the cause for better debugging.
🔎 Proposed fix to preserve exception cause
} catch (e: UnsatisfiedLinkError) {
Log.e(TAG, " ✗ Failed to load required library: $name", e)
- throw UnsatisfiedLinkError(
+ throw UnsatisfiedLinkError(
"Required library '$name' could not be loaded. " +
"Error: ${e.message}\n\n" +
"This may indicate:\n" +
" 1. Missing .so file in jniLibs/\n" +
" 2. Incorrect ABI architecture\n" +
" 3. Missing dependencies\n\n" +
"Successfully loaded: ${loadedLibraries.joinToString(", ")}"
- )
+ ).initCause(e)
}📝 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.
| } catch (e: UnsatisfiedLinkError) { | |
| Log.e(TAG, " ✗ Failed to load required library: $name", e) | |
| throw UnsatisfiedLinkError( | |
| "Required library '$name' could not be loaded. " + | |
| "Error: ${e.message}\n\n" + | |
| "This may indicate:\n" + | |
| " 1. Missing .so file in jniLibs/\n" + | |
| " 2. Incorrect ABI architecture\n" + | |
| " 3. Missing dependencies\n\n" + | |
| "Successfully loaded: ${loadedLibraries.joinToString(", ")}" | |
| ) | |
| } | |
| } catch (e: UnsatisfiedLinkError) { | |
| Log.e(TAG, " ✗ Failed to load required library: $name", e) | |
| throw UnsatisfiedLinkError( | |
| "Required library '$name' could not be loaded. " + | |
| "Error: ${e.message}\n\n" + | |
| "This may indicate:\n" + | |
| " 1. Missing .so file in jniLibs/\n" + | |
| " 2. Incorrect ABI architecture\n" + | |
| " 3. Missing dependencies\n\n" + | |
| "Successfully loaded: ${loadedLibraries.joinToString(", ")}" | |
| ).initCause(e) | |
| } |
🧰 Tools
🪛 detekt (1.23.8)
[warning] 131-131: The caught exception is swallowed. The original exception could be lost.
(detekt.exceptions.SwallowedException)
🤖 Prompt for AI Agents
In
sdk/runanywhere-flutter/android/src/main/kotlin/ai/runanywhere/flutter/RunanywherePlugin.kt
around lines 131 to 142, the new UnsatisfiedLinkError being thrown discards the
original exception cause; preserve the original UnsatisfiedLinkError (e) as the
cause so the stack trace and diagnostics are retained by creating the new
UnsatisfiedLinkError, calling initCause(e) on it, and then throwing that
instance (or otherwise attach e as the cause) while keeping the existing
detailed message.
| private fun nativeForceSymbolLoading() { | ||
| // Use the bridge to force symbol loading | ||
| RunAnywhereBridge.forceSymbolLoading() | ||
| } |
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
🏁 Script executed:
#!/bin/bash
# Verify RunAnywhereBridge class exists in the codebase
fd -e kt -e java "RunAnywhereBridge" sdk/runanywhere-flutter/android/
# Search for class definition
rg -n "class RunAnywhereBridge|object RunAnywhereBridge" sdk/runanywhere-flutter/android/Repository: RunanywhereAI/runanywhere-sdks
Length of output: 56
🏁 Script executed:
#!/bin/bash
# Search entire codebase for RunAnywhereBridge references
rg "RunAnywhereBridge" sdk/runanywhere-flutter/android/ -n
# Also check the RunanywherePlugin.kt file for imports and context around line 167-170
head -30 sdk/runanywhere-flutter/android/src/main/kotlin/ai/runanywhere/flutter/RunanywherePlugin.kt
# Check if there are any native library bindings or JNI references
rg "external fun|native fun" sdk/runanywhere-flutter/android/ -nRepository: RunanywhereAI/runanywhere-sdks
Length of output: 3311
🏁 Script executed:
#!/bin/bash
# Check full imports and class structure
sed -n '1,100p' sdk/runanywhere-flutter/android/src/main/kotlin/ai/runanywhere/flutter/RunanywherePlugin.kt | cat -n
# Also check if RunAnywhereBridge exists in any other Kotlin files in the project
find sdk/runanywhere-flutter/android -name "*.kt" -type f | xargs grep -l "class RunAnywhereBridge\|object RunAnywhereBridge" 2>/dev/null || echo "No RunAnywhereBridge definition found"
# Check if there's a package-level function or if it's expected to be native
rg "package ai.runanywhere" sdk/runanywhere-flutter/android/ -A 50 | head -60Repository: RunanywhereAI/runanywhere-sdks
Length of output: 11907
RunAnywhereBridge does not exist in the Android codebase.
The call to RunAnywhereBridge.forceSymbolLoading() at line 169 will cause a compilation error. RunAnywhereBridge is an iOS-specific class. This plugin's own documentation states that Android uses System.loadLibrary() for symbol loading, not RunAnywhereBridge. Either remove this method or replace it with the Android-appropriate implementation.
🤖 Prompt for AI Agents
In
sdk/runanywhere-flutter/android/src/main/kotlin/ai/runanywhere/flutter/RunanywherePlugin.kt
around lines 167 to 170, the nativeForceSymbolLoading() implementation calls the
iOS-only RunAnywhereBridge.forceSymbolLoading(), which does not exist on Android
and causes a compile error; replace this call with the Android-appropriate
symbol loading using System.loadLibrary("<native-lib-name>") (use the actual
native library name used by the plugin, e.g., "runanywhere"), and wrap the call
in a try/catch that logs or rethrows a clear exception on failure, or simply
remove the method if symbol loading is handled elsewhere.
| "getDiagnostics" -> { | ||
| // Get platform diagnostics (for debugging) | ||
| try { | ||
| val diagnostics = PlatformUtils.getDiagnostics(context) | ||
| result.success(diagnostics) | ||
| } catch (e: Exception) { | ||
| result.error("DIAGNOSTICS_ERROR", e.message, e.toString()) | ||
| } | ||
| } | ||
|
|
||
| "getCurrentAbi" -> { | ||
| // Get current ABI | ||
| result.success(PlatformUtils.getCurrentAbi()) | ||
| } | ||
|
|
||
| "getSupportedAbis" -> { | ||
| // Get all supported ABIs | ||
| result.success(PlatformUtils.getSupportedAbis()) | ||
| } | ||
|
|
||
| "getDeviceInfo" -> { | ||
| // Get device manufacturer and model | ||
| result.success(PlatformUtils.getDeviceInfo()) | ||
| } | ||
|
|
||
| "getNativeLibraryDir" -> { | ||
| // Get native library directory | ||
| result.success(PlatformUtils.getNativeLibraryDir(context)) |
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
🏁 Script executed:
#!/bin/bash
# Search for PlatformUtils class definition in the Flutter Android plugin
echo "=== Searching for PlatformUtils class files ==="
fd -e kt "PlatformUtils" sdk/runanywhere-flutter/android/
echo -e "\n=== Searching for PlatformUtils class definition ==="
rg -n "class PlatformUtils|object PlatformUtils" sdk/runanywhere-flutter/android/
echo -e "\n=== Checking imports of PlatformUtils in RunanywherePlugin ==="
rg -n "PlatformUtils" sdk/runanywhere-flutter/android/src/main/kotlin/ai/runanywhere/flutter/ -A 2 -B 2Repository: RunanywhereAI/runanywhere-sdks
Length of output: 3532
🏁 Script executed:
#!/bin/bash
# Search entire sdk directory for PlatformUtils
echo "=== Searching entire sdk/ for PlatformUtils ==="
rg "PlatformUtils" sdk/
echo -e "\n=== Checking imports at top of RunanywherePlugin.kt ==="
head -50 sdk/runanywhere-flutter/android/src/main/kotlin/ai/runanywhere/flutter/RunanywherePlugin.kt | grep -E "^import|^package"Repository: RunanywhereAI/runanywhere-sdks
Length of output: 12138
🏁 Script executed:
#!/bin/bash
# Check full import section of RunanywherePlugin.kt
echo "=== Full imports in RunanywherePlugin.kt ==="
head -30 sdk/runanywhere-flutter/android/src/main/kotlin/ai/runanywhere/flutter/RunanywherePlugin.kt
echo -e "\n=== PlatformUtils definition in commonMain ==="
cat sdk/runanywhere-kotlin/src/commonMain/kotlin/com/runanywhere/sdk/utils/PlatformUtils.kt | head -50
echo -e "\n=== PlatformUtils actual implementation in androidMain ==="
cat sdk/runanywhere-kotlin/src/androidMain/kotlin/com/runanywhere/sdk/utils/PlatformUtils.kt | head -80Repository: RunanywhereAI/runanywhere-sdks
Length of output: 4906
🏁 Script executed:
#!/bin/bash
# Double-check: search for these specific methods across entire codebase
echo "=== Searching for getDiagnostics method ==="
rg "fun getDiagnostics" sdk/
echo -e "\n=== Searching for getCurrentAbi method ==="
rg "fun getCurrentAbi" sdk/
echo -e "\n=== Searching for getSupportedAbis method ==="
rg "fun getSupportedAbis" sdk/
echo -e "\n=== Searching for getNativeLibraryDir method ==="
rg "fun getNativeLibraryDir" sdk/Repository: RunanywhereAI/runanywhere-sdks
Length of output: 258
Implement missing PlatformUtils methods.
PlatformUtils class exists in sdk/runanywhere-kotlin, but the Flutter Android plugin calls four methods that are not implemented: getDiagnostics(context), getCurrentAbi(), getSupportedAbis(), and getNativeLibraryDir(context). Only getDeviceInfo() exists. These Android-specific platform diagnostics methods must be added to the androidMain implementation of PlatformUtils to prevent compilation errors in the Flutter plugin.
🤖 Prompt for AI Agents
In sdk/runanywhere-kotlin androidMain implement the missing PlatformUtils
methods referenced by
sdk/runanywhere-flutter/android/src/main/kotlin/ai/runanywhere/flutter/RunanywherePlugin.kt
(around lines 295-322): add getDiagnostics(context), getCurrentAbi(),
getSupportedAbis(), and getNativeLibraryDir(context). Implement
getDiagnostics(context) to collect and return a simple diagnostics payload (OS
version, sdkInt, manufacturer, model, ABIs, nativeLibraryDir) in the same return
type used by getDeviceInfo; implement getCurrentAbi() to return the primary ABI
(Build.SUPPORTED_ABIS.firstOrNull() ?: Build.CPU_ABI); implement
getSupportedAbis() to return Build.SUPPORTED_ABIS as a List<String>; and
implement getNativeLibraryDir(context) to return
context.applicationInfo.nativeLibraryDir. Ensure method signatures and return
types exactly match the Flutter plugin calls and handle nulls safely to avoid
runtime compilation errors.
| // Check memory availability (if service is available) | ||
| if (memoryService != null) { | ||
| final memoryRequired = | ||
| modelInfo.memoryRequired ?? 1024 * 1024 * 1024; // Default 1GB | ||
| final canAllocate = await memoryService!.canAllocate(memoryRequired); | ||
| if (!canAllocate) { | ||
| logger.warning( | ||
| 'Memory might be insufficient: ${memoryRequired ~/ (1024 * 1024)}MB required'); | ||
| } | ||
| } |
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.
Insufficient memory only warns but doesn't prevent loading.
When canAllocate returns false, only a warning is logged (lines 110-112), and the model load continues. This could lead to out-of-memory crashes at runtime. Consider either:
- Failing the load when memory is insufficient, or
- Documenting this behavior if it's intentional (e.g., memory check is advisory only).
🔎 Proposed fix to fail on insufficient memory
if (memoryService != null) {
final memoryRequired =
modelInfo.memoryRequired ?? 1024 * 1024 * 1024; // Default 1GB
final canAllocate = await memoryService!.canAllocate(memoryRequired);
if (!canAllocate) {
- logger.warning(
- 'Memory might be insufficient: ${memoryRequired ~/ (1024 * 1024)}MB required');
+ logger.error(
+ 'Insufficient memory: ${memoryRequired ~/ (1024 * 1024)}MB required');
+ throw SDKError.modelLoadFailed(
+ modelId,
+ 'Insufficient memory to load model: ${memoryRequired ~/ (1024 * 1024)}MB required',
+ );
}
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
sdk/runanywhere-flutter/lib/capabilities/model_loading/model_loading_service.dart
around lines 104 to 113, the memory check logs a warning when canAllocate is
false but continues loading; change this to abort the load by returning a failed
Future or throwing a descriptive exception (e.g., ModelLoadException or
Future.error) so the caller knows loading failed due to insufficient memory.
Include the required memory amount in the error message, ensure any upstream
callers handle the thrown error or error result, and add/update a unit test to
cover the insufficient-memory path.
| // TODO: Stub for MemoryService until proper memory management is implemented | ||
| // This should be moved to lib/capabilities/memory/memory_service.dart | ||
| class MemoryService { | ||
| Future<bool> canAllocate(int bytes) async { | ||
| // Stub: Always return true for now | ||
| // In a real implementation, this would check available memory | ||
| return true; | ||
| } | ||
| } |
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.
Stub MemoryService defeats memory safety and violates architecture.
This stub has several critical issues:
- Always returns
true: Completely bypasses memory checking, defeating the safety mechanism designed to prevent OOM crashes. - Wrong location: The stub is defined in
model_loading_service.dartinstead oflib/capabilities/memory/memory_service.dartas noted in the TODO (line 326), violating separation of concerns. - Production risk: Combined with the warning-only behavior in lines 104-113, memory checks are completely non-functional.
Recommendations:
- Move the stub to the correct location:
lib/capabilities/memory/memory_service.dart - Consider implementing a basic memory check (e.g., using
dart:ioto query system memory) rather than always returningtrue - If this is temporary tech debt for the restructuring, ensure it's tracked and prioritized
Would you like me to open an issue to track proper MemoryService implementation with basic memory checking capabilities?
| CapabilityLoadingState get state { | ||
| if (_loadedResourceId != null) { | ||
| return CapabilityLoaded(resourceId: _loadedResourceId!); | ||
| } | ||
| if (_inflightLoad != null) { | ||
| return const CapabilityLoading(resourceId: ''); | ||
| } | ||
| return const CapabilityIdle(); | ||
| } |
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.
state getter returns empty resourceId during loading.
When a load is in progress, the CapabilityLoading state is created with an empty string for resourceId. This loses the information about which resource is being loaded, which may be useful for UI progress indicators.
🔎 Proposed fix to track the loading resourceId
Add a field to track the in-flight resource ID:
Completer<ServiceType>? _inflightLoad;
+String? _inflightResourceId;
ComponentConfiguration? _configuration;Update the state getter:
CapabilityLoadingState get state {
if (_loadedResourceId != null) {
return CapabilityLoaded(resourceId: _loadedResourceId!);
}
if (_inflightLoad != null) {
- return const CapabilityLoading(resourceId: '');
+ return CapabilityLoading(resourceId: _inflightResourceId ?? '');
}
return const CapabilityIdle();
}Set _inflightResourceId = resourceId when starting load, and clear it on completion/failure.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In sdk/runanywhere-flutter/lib/core/capabilities/managed_lifecycle.dart around
lines 93 to 101, the state getter builds CapabilityLoading with an empty
resourceId losing the id of the in-flight load; add a private field (e.g.
_inflightResourceId) to hold the resourceId for the active load, change the
state getter to return CapabilityLoading(resourceId: _inflightResourceId ?? ''),
set _inflightResourceId = resourceId when you start a load, and clear it (null)
on load completion or failure so the loading state carries the correct
resourceId.
| /// Reset all state, cancelling any in-flight loads. | ||
| Future<void> reset() async { | ||
| if (_loadedResourceId != null) { | ||
| _trackEvent(_LifecycleEventType.unloaded, _loadedResourceId!); | ||
| } | ||
|
|
||
| _inflightLoad = null; | ||
|
|
||
| if (_service != null) { | ||
| await _unloadResource(_service as ServiceType); | ||
| } | ||
|
|
||
| _service = null; | ||
| _loadedResourceId = null; | ||
| _configuration = null; | ||
| } |
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.
reset() nulls _inflightLoad before completing it, leaving awaiters hanging.
If load() is in progress and another caller awaits _inflightLoad!.future (line 131), calling reset() will null out the completer without completing it. The awaiting code will never resolve.
🔎 Proposed fix to complete the completer with an error
Future<void> reset() async {
if (_loadedResourceId != null) {
_trackEvent(_LifecycleEventType.unloaded, _loadedResourceId!);
}
+ // Complete in-flight load with error before nulling
+ if (_inflightLoad != null && !_inflightLoad!.isCompleted) {
+ _inflightLoad!.completeError(
+ const OperationFailedError('load', 'Reset called during load'),
+ );
+ }
_inflightLoad = null;
if (_service != null) {
await _unloadResource(_service as ServiceType);
}📝 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.
| /// Reset all state, cancelling any in-flight loads. | |
| Future<void> reset() async { | |
| if (_loadedResourceId != null) { | |
| _trackEvent(_LifecycleEventType.unloaded, _loadedResourceId!); | |
| } | |
| _inflightLoad = null; | |
| if (_service != null) { | |
| await _unloadResource(_service as ServiceType); | |
| } | |
| _service = null; | |
| _loadedResourceId = null; | |
| _configuration = null; | |
| } | |
| /// Reset all state, cancelling any in-flight loads. | |
| Future<void> reset() async { | |
| if (_loadedResourceId != null) { | |
| _trackEvent(_LifecycleEventType.unloaded, _loadedResourceId!); | |
| } | |
| // Complete in-flight load with error before nulling | |
| if (_inflightLoad != null && !_inflightLoad!.isCompleted) { | |
| _inflightLoad!.completeError( | |
| const OperationFailedError('load', 'Reset called during load'), | |
| ); | |
| } | |
| _inflightLoad = null; | |
| if (_service != null) { | |
| await _unloadResource(_service as ServiceType); | |
| } | |
| _service = null; | |
| _loadedResourceId = null; | |
| _configuration = null; | |
| } |
🤖 Prompt for AI Agents
In sdk/runanywhere-flutter/lib/core/capabilities/managed_lifecycle.dart around
lines 207 to 222, reset() currently sets _inflightLoad = null before completing
any existing completer which can leave awaiters hanging; modify reset() to check
if _inflightLoad != null and, if so, complete it with an error (e.g., StateError
or a custom cancellation error) or completeError with a descriptive message,
then set _inflightLoad = null; ensure completion happens before nulling the
field and that any exceptions are caught/logged if needed.
| // MARK: - Metrics | ||
|
|
||
| /// Get lifecycle metrics | ||
| ModelLifecycleMetrics getLifecycleMetrics() { | ||
| return ModelLifecycleMetrics( | ||
| totalEvents: _loadCount, | ||
| startTime: _startTime, | ||
| lastEventTime: null, | ||
| totalLoads: _loadCount, | ||
| successfulLoads: _loadCount, | ||
| failedLoads: 0, | ||
| averageLoadTimeMs: _loadCount > 0 ? _totalLoadTime / _loadCount : 0, | ||
| totalUnloads: 0, | ||
| totalDownloads: 0, | ||
| successfulDownloads: 0, | ||
| failedDownloads: 0, | ||
| totalBytesDownloaded: 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.
Lifecycle metrics are incomplete - failures and unloads are not tracked.
The getLifecycleMetrics() method always returns 0 for failedLoads and totalUnloads, even though the class handles both scenarios. This makes the metrics unreliable for monitoring.
🔎 Proposed fix to track all lifecycle events
Add tracking fields:
int _loadCount = 0;
+int _failedLoadCount = 0;
+int _unloadCount = 0;
double _totalLoadTime = 0;Update load() catch block:
} catch (error) {
final loadTimeMs =
DateTime.now().difference(startTime).inMilliseconds.toDouble();
_inflightLoad = null;
+ _failedLoadCount += 1;Update unload():
_service = null;
_loadedResourceId = null;
+_unloadCount += 1;Update getLifecycleMetrics():
return ModelLifecycleMetrics(
totalEvents: _loadCount,
startTime: _startTime,
lastEventTime: null,
totalLoads: _loadCount,
- successfulLoads: _loadCount,
- failedLoads: 0,
+ successfulLoads: _loadCount - _failedLoadCount,
+ failedLoads: _failedLoadCount,
averageLoadTimeMs: _loadCount > 0 ? _totalLoadTime / _loadCount : 0,
- totalUnloads: 0,
+ totalUnloads: _unloadCount,Committable suggestion skipped: line range outside the PR's diff.
| bool get supportsLLM { | ||
| switch (this) { | ||
| case InferenceFramework.llamaCpp: | ||
| case InferenceFramework.mlx: | ||
| case InferenceFramework.coreML: | ||
| case InferenceFramework.onnx: | ||
| case InferenceFramework.foundationModels: | ||
| case InferenceFramework.picoLLM: | ||
| case InferenceFramework.mlc: | ||
| return true; | ||
| default: | ||
| return false; | ||
| } | ||
| } |
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
🏁 Script executed:
# Find the Flutter inference_framework.dart file
find . -name "inference_framework.dart" -type f | head -5
# Also search for iOS InferenceFramework.swift
find . -name "InferenceFramework.swift" -type f | head -5Repository: RunanywhereAI/runanywhere-sdks
Length of output: 254
🏁 Script executed:
# Read the Flutter InferenceFramework file
cat -n ./sdk/runanywhere-flutter/lib/core/module/inference_framework.dart
# Read the iOS InferenceFramework file
cat -n ./sdk/runanywhere-swift/Sources/RunAnywhere/Infrastructure/ModelManagement/Models/Domain/InferenceFramework.swiftRepository: RunanywhereAI/runanywhere-sdks
Length of output: 6112
🏁 Script executed:
# Search for any tests related to supportsLLM
rg -n "supportsLLM" --type dart --type swift -B2 -A2
# Search for any architecture documentation or comments about framework selection
rg -n "swiftTransformers|execuTorch|tensorFlowLite" --type dart --type swift -B3 -A3
# Check for any test files that might explain the mapping
find . -name "*test*" -o -name "*spec*" | grep -i "framework\|inference" | head -10Repository: RunanywhereAI/runanywhere-sdks
Length of output: 16900
🏁 Script executed:
# Search for any documentation or comments explaining these framework mappings
rg -n "supportsLLM|LLM.*support|text-to-text" --type swift --type dart -B5 -A2 | grep -A5 -B5 "execuTorch\|swiftTransformers\|tensorFlowLite"
# Check git history or PR comments about these frameworks
git log --oneline --all -S "supportsLLM" -- sdk/runanywhere-swift/ sdk/runanywhere-flutter/ | head -10Repository: RunanywhereAI/runanywhere-sdks
Length of output: 143
🏁 Script executed:
# Check the commit that modified supportsLLM
git show 3ddc676e --stat | head -30
# Get more details about what changed in supportsLLM
git show 3ddc676e | grep -A20 -B20 "supportsLLM" | head -60Repository: RunanywhereAI/runanywhere-sdks
Length of output: 4084
🏁 Script executed:
# Check for README files with framework documentation
find . -name "README*" -type f | head -10
# Search for any architecture or design documentation
find . -name "*ARCH*" -o -name "*DESIGN*" -o -name "*FRAMEWORK*" | grep -i doc | head -10
# Look for any comments in the code explaining the mapping decisions
rg -n "LLM.*capability|framework.*support|execuTorch|swiftTransformers" --type swift --type dart -B10 | grep -i "why\|reason\|note\|important" | head -20Repository: RunanywhereAI/runanywhere-sdks
Length of output: 661
Reconcile LLM capability mappings across framework categories.
The supportsLLM getter excludes swiftTransformers, execuTorch, and tensorFlowLite, but these same frameworks are classified as .language models in ModelCategory (both iOS and Flutter) and define textToText capabilities in FrameworkModality. This inconsistency causes contradictory behavior in framework selection and capability detection. Either supportsLLM should include these frameworks or their language/text-to-text classifications should be removed from ModelCategory and FrameworkModality to maintain consistency across the codebase.
🤖 Prompt for AI Agents
sdk/runanywhere-flutter/lib/core/module/inference_framework.dart around lines 49
to 62: the supportsLLM getter currently omits swiftTransformers, execuTorch, and
tensorFlowLite while they are classified as .language and textToText elsewhere,
creating inconsistent capability mapping; to fix, update the supportsLLM switch
to include InferenceFramework.swiftTransformers, InferenceFramework.execuTorch,
and InferenceFramework.tensorFlowLite (or alternatively remove their
.language/textToText classifications in ModelCategory/FrameworkModality) so that
framework LLM support and language/text-to-text classifications are consistent
across the codebase.
| { | ||
| "sdk_root": "sdk/runanywhere-flutter", | ||
| "platform": "flutter", | ||
| "iteration": 4, | ||
| "date": "2025-12-20", | ||
| "status": "complete", | ||
| "commands": [ | ||
| {"cmd": "dart format lib/", "status": "ok"}, | ||
| {"cmd": "flutter analyze lib/", "status": "ok"}, | ||
| {"cmd": "./tool/quality/todo_check.sh", "status": "fail"} | ||
| ], | ||
| "issues_before": { | ||
| "total_files": 227, | ||
| "analyzer": 0, | ||
| "warnings": 2, | ||
| "info": 22, | ||
| "format": 0, | ||
| "todo_missing_refs": 16, | ||
| "unused_files": 12 | ||
| }, | ||
| "issues_after": { | ||
| "total_files": 215, | ||
| "analyzer": 0, | ||
| "warnings": 2, | ||
| "info": 22, | ||
| "format": 0, | ||
| "todo_missing_refs": 16, | ||
| "unused_files": 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.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify file exists
test -f sdk/runanywhere-flutter/QUALITY_REPORT.json && echo "File exists" || echo "File not found"
# Count actual TODOs without issue references in the Flutter SDK
echo "=== TODOs without issue references ==="
rg -n 'TODO' sdk/runanywhere-flutter/lib/ 2>/dev/null | grep -v -E '#[0-9]+|JIRA-|ISSUE-|GH-|github' | wc -l
echo "=== Sample TODOs without references (first 15) ==="
rg -n 'TODO' sdk/runanywhere-flutter/lib/ 2>/dev/null | grep -v -E '#[0-9]+|JIRA-|ISSUE-|GH-|github' | head -15Repository: RunanywhereAI/runanywhere-sdks
Length of output: 2406
Update QUALITY_REPORT.json: TODO count is inaccurate
The report shows 16 TODOs missing issue references, but actual code scanning found 17 untracked TODOs. Update the metrics to reflect the current state. The discrepancy suggests the report may be outdated and should be regenerated.
Additionally, while marking status as "complete" despite todo_check.sh failing is acceptable since failures are documented in remaining_blockers, the persistent 17 untracked TODOs represent ongoing technical debt that should be prioritized (examples: sentry integration stubs, incomplete features in logging and streaming services).
🤖 Prompt for AI Agents
In sdk/runanywhere-flutter/QUALITY_REPORT.json around lines 1 to 29, the TODO
count is stale: change "todo_missing_refs": 16 to 17 in both "issues_before" and
"issues_after" (or regenerate the report via ./tool/quality/todo_check.sh to
refresh all metrics) so the JSON reflects the 17 untracked TODOs found by the
scanner; rerun the quality pipeline and commit the updated QUALITY_REPORT.json.
- Updated backend service files for LlamaCPP and ONNX to improve organization and clarity. - Introduced new service exports for LlamaCPP and ONNX, enhancing modularity. - Enhanced analytics services for LLM, STT, TTS, and VAD to improve performance tracking. - Cleaned up imports and refactored code for better maintainability across various modules. - Added documentation and comments for improved code understanding and usability.
- Added Sqflite plugin registration for both Android and iOS platforms to support local database functionality. - Updated TTS model loading methods to TTS voice for consistency across the SDK. - Refactored related debug print statements and method names to reflect the new terminology. - Cleaned up unused imports in audio services for improved code clarity and maintainability.
- Replaced direct access to model IDs with convenience getters in VoiceAgentComponentStates for improved readability. - Updated voice_assistant_view.dart to utilize the new getters for STT, LLM, and TTS model names, enhancing code maintainability.
…e SDK - Updated chat_interface_view.dart to replace direct SDK calls with namespaced imports for clarity and maintainability. - Enhanced model selection and settings management in combined_settings_view.dart to integrate with RunAnywhere SDK for storage operations and model handling. - Improved storage data loading and management, including methods for clearing cache and cleaning temporary files. - Refactored UI components for better organization and user experience, including the addition of a downloaded models section and improved API key management.
- Added url_launcher dependency to pubspec.yaml for improved URL handling. - Updated Android build.gradle and settings.gradle to use Gradle version 8.9.1. - Modified GeneratedPluginRegistrant.java to register the UrlLauncherPlugin. - Enhanced iOS GeneratedPluginRegistrant.m to include URLLauncherPlugin registration. - Refactored model artifact type handling in runanywhere_ai_app.dart for better structure. - Improved settings management in combined_settings_view.dart, including model storage updates and removal of unused code.
…es, add new Dart bridge files, and introduce new types for enhanced functionality. This update streamlines the codebase and prepares for future enhancements.
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
Improvements
Removals
✏️ Tip: You can customize this high-level summary in your review settings.
Greptile Summary
This PR implements a major architectural refactoring of the Flutter SDK to achieve iOS parity, transitioning from a component-based to a capability-based architecture with enhanced lifecycle management, analytics, and networking capabilities.
Key Changes
ComponenttoCapabilitypattern withManagedLifecyclefor unified resource management and automatic event trackingAPIClient,AuthenticationServicewith token refresh, and Supabase-compatible headersGenerationAnalyticsService,STTAnalyticsService,TTSAnalyticsService,VADAnalyticsServicewith TTFT trackingRunAnywhereModuleprotocol with storage strategies and download strategies for better extensibilityVoiceSessionHandlewith event-driven architecture for voice interaction controlTelemetryRepository,SyncCoordinator, andAnalyticsQueueManagerfor offline-first analyticsFileManagementServiceandSimplifiedFileManagerfor centralized file operationsQUALITY_REPORT.md,PARITY_REPORT.md) and TODO checking scriptsRemoved Code
native_backend.dart,platform_loader.dart,ffi_types.dart- 2,066 lines)tts_component.dart,vlm_component.dart,wake_word_component.dart,speaker_diarization_component.dart- 1,746 lines)Architecture Benefits
EventPublisherConfidence Score: 4/5
authentication_service.dartfor token refresh edge cases,streaming_service.dartfor proper cleanup of background tasks, andvoice_session_handle.dartfor callback null safetyImportant Files Changed
Sequence Diagram
sequenceDiagram participant App as Application participant SDK as RunAnywhere participant Reg as ModuleRegistry participant Cap as Capability participant LC as Lifecycle participant Metrics as Metrics participant Events as Events Note over App,Events: SDK Initialization App->>SDK: initialize(configuration) SDK->>SDK: Validate and setup SDK-->>App: Initialized App->>SDK: completeServicesInit() SDK->>SDK: Setup services SDK->>Metrics: Initialize SDK->>Events: Setup SDK-->>App: Services Ready Note over App,Events: Capability Loading App->>Reg: registerModule(module) Reg->>Reg: Store providers App->>SDK: createLLM(config) SDK->>Cap: Initialize Cap->>LC: load(modelId) LC->>Events: LoadStarted LC->>Reg: Get provider Reg-->>LC: Provider LC->>LC: Load resource LC->>Metrics: Track metrics LC->>Events: LoadCompleted LC-->>Cap: Ready Cap-->>App: Capability ready Note over App,Events: Streaming Generation App->>SDK: generateStream(prompt) SDK->>Cap: generateStream() Cap->>Metrics: startGeneration() Metrics-->>Cap: generationId Cap->>LC: Get service LC-->>Cap: Service loop For each token Cap->>Metrics: Track TTFT Cap->>App: Emit token end Cap->>Metrics: Complete Metrics->>Events: GenerationCompleted Cap-->>App: Done Note over App,Events: Voice Session App->>SDK: startVoiceSession() SDK->>Cap: Initialize VoiceAgent Cap->>Cap: Load components Cap->>LC: Load STT/LLM/TTS/VAD LC->>Events: Component events Cap-->>SDK: SessionHandle App->>SDK: handle.start() SDK->>Events: SessionStarted loop Audio Processing App->>SDK: feedAudio(audioData) SDK->>Cap: processAudio() Cap->>Cap: VAD detection Cap->>Cap: STT transcription Cap->>Cap: LLM generation Cap->>Cap: TTS synthesis Cap->>Events: Component events Cap-->>App: VoiceAgentResult end App->>SDK: handle.stop() SDK->>Events: SessionStoppedContext used:
dashboard- CLAUDE.md (source)