feat: support to build for windows and add proxy when do http request…#495
feat: support to build for windows and add proxy when do http request…#495peilinok wants to merge 1 commit intoRunanywhereAI:mainfrom
Conversation
|
Too many files changed for review. ( |
📝 WalkthroughWalkthroughThis PR adds comprehensive Windows desktop support to the RunAnywhereAI Flutter application and SDK, introducing a new Windows build pipeline (CMake-based native runner and plugins), a flexible proxy configuration system with dual scopes, vision/VLM camera abstraction, and platform capability gating to conditionally enable features per platform. Changes
Sequence Diagram(s)sequenceDiagram
participant App as Flutter App
participant ProxySvc as ProxySettingsService
participant ExHttpSvc as ExampleHttpService
participant HttpClient as HttpClient
participant Network as Network/API
App->>ProxySvc: load(ProxyScope)
ProxySvc->>ProxySvc: read SharedPrefs + Keychain
ProxySvc-->>App: ProxySettings
App->>App: user configures proxy
App->>ProxySvc: save(scope, settings)
ProxySvc->>ProxySvc: validate(settings)
ProxySvc->>ProxySvc: persist to SharedPrefs + Keychain
ProxySvc-->>App: validation result
App->>ExHttpSvc: testProxy(scope, uri)
ExHttpSvc->>ProxySvc: load(scope)
ProxySvc-->>ExHttpSvc: ProxySettings
ExHttpSvc->>HttpClient: createScopedHttpClient(scope)
HttpClient->>HttpClient: configure with ProxySettings
HttpClient->>Network: HTTP GET request
Network-->>HttpClient: 2xx response
HttpClient-->>ExHttpSvc: success
ExHttpSvc-->>App: test result
sequenceDiagram
participant VLMView as VLMView
participant VLMViewModel as VLMViewModel
participant CameraBackend as VisionCameraBackend
participant CameraSession as VisionCameraSession
participant VLMService as VisionVlmService
participant SDK as RunAnywhere SDK
VLMView->>VLMViewModel: init with injected backends
VLMViewModel->>CameraBackend: listDevices()
CameraBackend-->>VLMViewModel: List<VisionCameraDevice>
VLMViewModel->>CameraBackend: createSession(device)
CameraBackend-->>VLMViewModel: VisionCameraSession
VLMViewModel->>CameraSession: initialize()
CameraSession-->>VLMViewModel: initialized
VLMView->>VLMViewModel: captureAndDescribeImage()
VLMViewModel->>CameraSession: captureStill()
CameraSession-->>VLMViewModel: imagePath
VLMViewModel->>VLMService: processImageStream(imagePath, prompt)
VLMService->>SDK: loadModel(modelId)
SDK-->>VLMService: model loaded
VLMService->>SDK: processImageStream(VLMImage, options)
SDK-->>VLMService: Stream<String> tokens
VLMService-->>VLMViewModel: Stream<String>
VLMViewModel->>VLMViewModel: yield tokens, update description
VLMViewModel-->>VLMView: currentDescription
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Pull request overview
This PR adds an initial Windows “vertical slice” for the Flutter SDK and example app, and introduces configurable proxy support for HTTP model downloads.
Changes:
- Add Windows plugin implementations/CMake bundling for
runanywhere,runanywhere_llamacpp, andrunanywhere_onnx. - Add proxy-configurable HTTP client factory for model downloads (SDK) plus example app proxy services/settings.
- Update runanywhere-commons for Windows build workflows (MSVC build script, filesystem fixes, model-path selection, ONNX/llama.cpp Windows tweaks) and add Windows app runner scaffolding.
Reviewed changes
Copilot reviewed 88 out of 125 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin_c_api.cpp | Adds Windows C API registration shim for the ONNX Flutter plugin. |
| sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.h | Declares Windows plugin class for ONNX method channel. |
| sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.cpp | Implements basic Windows method-channel handlers for ONNX plugin metadata. |
| sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/include/runanywhere_onnx/runanywhere_onnx_plugin_c_api.h | Exposes C API header for Windows registration. |
| sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/CMakeLists.txt | Builds plugin DLL and stages ONNX runtime DLL dependencies when available. |
| sdk/runanywhere-flutter/packages/runanywhere_onnx/pubspec.yaml | Registers Windows platform for ONNX Flutter plugin. |
| sdk/runanywhere-flutter/packages/runanywhere_onnx/lib/native/onnx_bindings.dart | Loads rac_backend_onnx on Windows via PlatformLoader. |
| sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin_c_api.cpp | Adds Windows C API registration shim for llama.cpp Flutter plugin. |
| sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.h | Declares Windows plugin class for llama.cpp method channel. |
| sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cpp | Implements Windows method-channel handlers for llama.cpp plugin metadata. |
| sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/include/runanywhere_llamacpp/runanywhere_llamacpp_plugin_c_api.h | Exposes C API header for Windows registration. |
| sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/CMakeLists.txt | Builds plugin DLL and requires/stages rac_backend_llamacpp.dll. |
| sdk/runanywhere-flutter/packages/runanywhere_llamacpp/pubspec.yaml | Registers Windows platform for llama.cpp Flutter plugin. |
| sdk/runanywhere-flutter/packages/runanywhere_llamacpp/lib/native/llamacpp_bindings.dart | Loads rac_backend_llamacpp on Windows via PlatformLoader. |
| sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin_c_api.cpp | Adds Windows C API registration shim for core RunAnywhere plugin. |
| sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.h | Declares Windows plugin class for core RunAnywhere method channel. |
| sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.cpp | Implements Windows method-channel handlers for SDK/commons version metadata. |
| sdk/runanywhere-flutter/packages/runanywhere/windows/include/runanywhere/runanywhere_plugin_c_api.h | Exposes C API header for Windows registration. |
| sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt | Builds plugin DLL and requires/stages rac_commons.dll. |
| sdk/runanywhere-flutter/packages/runanywhere/test/vlm_file_resolution_test.dart | Adds tests for Windows-safe basename and VLM file resolution. |
| sdk/runanywhere-flutter/packages/runanywhere/pubspec.yaml | Registers Windows platform for core RunAnywhere Flutter plugin. |
| sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart | Exposes configureDownloadHttpClientFactory and routes VLM path resolution via helper. |
| sdk/runanywhere-flutter/packages/runanywhere/lib/native/platform_loader.dart | Improves Windows DLL lookup by checking the executable directory first. |
| sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_tts.dart | Avoids accessing handle fields directly inside Isolate.run closure. |
| sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_stt.dart | Avoids accessing handle fields directly inside Isolate.run closure. |
| sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_model_registry.dart | Normalizes Windows path separators when listing directory entries. |
| sdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dart | Adds Windows-safe VLM file resolution helpers. |
| sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart | Adds injectable HTTP client factory for downloads (proxy support) and per-file client lifecycle. |
| sdk/runanywhere-flutter/packages/runanywhere/README.md | Documents Windows setup steps and marks Windows support as preview. |
| sdk/runanywhere-commons/tests/test_download_orchestrator.cpp | Adds test to ensure model resolution prefers primary .gguf over mmproj*.gguf. |
| sdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction_stub.cpp | Adds a stub extraction implementation for builds without native extraction. |
| sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp | Adds Windows std::filesystem paths/dir listing, skips auxiliary model files, and improves nested-directory selection logic. |
| sdk/runanywhere-commons/src/core/rac_core.cpp | Restricts diffusion registry initialization to iOS/macOS (not “non-Android”). |
| sdk/runanywhere-commons/src/backends/onnx/rac_backend_onnx_register.cpp | Adds Windows-friendly directory handling in can_handle and compile-time gating for embeddings registration. |
| sdk/runanywhere-commons/src/backends/onnx/onnx_backend.cpp | Avoids dirent.d_type reliance by using stat for directory diagnostics. |
| sdk/runanywhere-commons/src/backends/onnx/CMakeLists.txt | Introduces RAC_ONNX_EMBEDDINGS_AVAILABLE define and Windows build messaging. |
| sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt | Adds Windows build configuration and toggles VLM mtmd based on RAC_BUILD_VLM. |
| sdk/runanywhere-commons/scripts/windows/download-sherpa-onnx.bat | Makes downloads/extraction more robust; normalizes Sherpa DLL placement and verifies expected artifacts. |
| sdk/runanywhere-commons/scripts/build-windows.bat | Replaces prior Windows build script with simplified backend selection + DLL staging flow. |
| sdk/runanywhere-commons/README.md | Documents Windows build usage and Windows ONNX dependency flow. |
| sdk/runanywhere-commons/CMakeLists.txt | Adds RAC_BUILD_VLM option for controlling llama.cpp multimodal build. |
| examples/flutter/RunAnywhereAI/windows/runner/win32_window.h | Adds Windows runner window abstraction (Flutter template). |
| examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp | Implements Win32 window plumbing + dark mode support (Flutter template). |
| examples/flutter/RunAnywhereAI/windows/runner/utils.h | Adds runner console/util declarations (Flutter template). |
| examples/flutter/RunAnywhereAI/windows/runner/utils.cpp | Implements console attach/create and arg conversion (Flutter template). |
| examples/flutter/RunAnywhereAI/windows/runner/runner.exe.manifest | Adds DPI awareness + supported OS manifest (Flutter template). |
| examples/flutter/RunAnywhereAI/windows/runner/resource.h | Adds Windows resource IDs (Flutter template). |
| examples/flutter/RunAnywhereAI/windows/runner/main.cpp | Adds Windows runner entry point + COM init (Flutter template). |
| examples/flutter/RunAnywhereAI/windows/runner/flutter_window.h | Adds Flutter-hosting window wrapper (Flutter template). |
| examples/flutter/RunAnywhereAI/windows/runner/flutter_window.cpp | Implements Flutter view controller hosting + message handling (Flutter template). |
| examples/flutter/RunAnywhereAI/windows/runner/Runner.rc | Adds Windows resources/version info (Flutter template). |
| examples/flutter/RunAnywhereAI/windows/runner/CMakeLists.txt | Adds runner build target, resources, and dependencies (Flutter template). |
| examples/flutter/RunAnywhereAI/windows/flutter/generated_plugins.cmake | Generated plugin list for Windows build (includes RunAnywhere plugins). |
| examples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.h | Generated plugin registrant header for Windows. |
| examples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.cc | Generated plugin registrant implementation for Windows. |
| examples/flutter/RunAnywhereAI/windows/flutter/CMakeLists.txt | Flutter-managed Windows build rules (template). |
| examples/flutter/RunAnywhereAI/windows/CMakeLists.txt | Project-level Windows build and install rules (template). |
| examples/flutter/RunAnywhereAI/windows/.gitignore | Adds Windows-specific ignore rules for example app. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/include/flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h | Vendored Windows secure storage plugin headers. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.h | Vendored Windows secure storage plugin headers (duplicate include path). |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/CMakeLists.txt | Vendored Windows secure storage plugin build rules. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/pubspec.yaml | Adds vendored federated Windows implementation package metadata. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dart | Adds non-Windows stub implementation for vendored plugin. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dart | Adds Windows DPAPI-backed implementation (vendored). |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/flutter_secure_storage_windows.dart | Adds conditional export for stub vs FFI implementation. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/win32_window.h | Vendored plugin example Windows runner files. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/win32_window.cpp | Vendored plugin example Windows runner files. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.h | Vendored plugin example Windows runner files. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.cpp | Vendored plugin example Windows runner files. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/runner.exe.manifest | Vendored plugin example Windows runner files. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/resource.h | Vendored plugin example Windows runner files. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/main.cpp | Vendored plugin example Windows runner files. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.h | Vendored plugin example Windows runner files. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.cpp | Vendored plugin example Windows runner files. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/Runner.rc | Vendored plugin example Windows runner files. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/CMakeLists.txt | Vendored plugin example Windows runner build rules. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugins.cmake | Vendored plugin example generated plugin listing. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugin_registrant.h | Vendored plugin example registrant header. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugin_registrant.cc | Vendored plugin example registrant implementation. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/CMakeLists.txt | Vendored plugin example Flutter-managed Windows build rules. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/CMakeLists.txt | Vendored plugin example project-level Windows build rules. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/pubspec.yaml | Vendored plugin example pubspec. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/lib/main.dart | Vendored plugin example app. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/analysis_options.yaml | Vendored plugin example analyzer config. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/README.md | Vendored plugin example readme. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/analysis_options.yaml | Vendored plugin analyzer config. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/README.md | Vendored plugin readme. |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/LICENSE | Vendored plugin license (BSD-3). |
| examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/CHANGELOG.md | Vendored plugin changelog. |
| examples/flutter/RunAnywhereAI/test/widget_test.dart | Avoids pumpAndSettle timeouts by using bounded pump calls. |
| examples/flutter/RunAnywhereAI/test/features/vision/vlm_view_model_test.dart | Adds VLM view model tests with fake camera + VLM service abstractions. |
| examples/flutter/RunAnywhereAI/pubspec.yaml | Pins Windows camera implementation and vendors secure storage Windows plugin; adjusts record version. |
| examples/flutter/RunAnywhereAI/lib/features/voice/voice_assistant_view.dart | Gates voice assistant UI on platform capability service. |
| examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart | Updates model state handling + gates TTS UI on platform capability service. |
| examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart | Updates model state refresh + gates STT UI on platform capability service. |
| examples/flutter/RunAnywhereAI/lib/features/vision/vlm_camera_view.dart | Switches to camera session abstraction and improves error presentation and permission CTA. |
| examples/flutter/RunAnywhereAI/lib/features/vision/vision_hub_view.dart | Gates Vision hub UI on platform capability service. |
| examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_vlm_service.dart | Introduces VLM service abstraction + RunAnywhere-backed implementation. |
| examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_permission_gateway.dart | Introduces permission gateway abstraction for camera permissions. |
| examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart | Introduces camera backend/session abstractions, wrapping camera plugin. |
| examples/flutter/RunAnywhereAI/lib/features/settings/tool_settings_view_model.dart | Routes example HTTP calls through proxy-aware ExampleHttpService. |
| examples/flutter/RunAnywhereAI/lib/features/rag/rag_demo_view.dart | Gates RAG demo UI on platform capability service. |
| examples/flutter/RunAnywhereAI/lib/features/models/model_selection_sheet.dart | Hides System TTS option on Windows. |
| examples/flutter/RunAnywhereAI/lib/features/models/model_list_view_model.dart | Rethrows model-load errors after notifying listeners. |
| examples/flutter/RunAnywhereAI/lib/features/chat/chat_interface_view.dart | Prevents navigation to unsupported RAG feature on Windows; minor logging/format changes. |
| examples/flutter/RunAnywhereAI/lib/core/utilities/constants.dart | Adds proxy preference/keychain key constants. |
| examples/flutter/RunAnywhereAI/lib/core/services/proxy_settings_service.dart | Adds persisted proxy settings service with validation + legacy migration path. |
| examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart | Introduces platform capability gating (notably disables RAG on Windows). |
| examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart | Adds proxy-aware HTTP client creation for example app network calls. |
| examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart | Adds proxy settings model types (scope/scheme/settings). |
| examples/flutter/RunAnywhereAI/lib/core/design_system/unsupported_feature_view.dart | Adds reusable “feature unavailable” UI component. |
| examples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dart | Adds withValues compatibility helper and adjusts semantic color mapping. |
| examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart | Configures proxy-aware HTTP client factory for model downloads and improves backend registration messaging. |
| examples/flutter/RunAnywhereAI/ios/Runner/GeneratedPluginRegistrant.m | Updates plugin registrant to match updated plugin names (record_darwin, sqflite). |
| examples/flutter/RunAnywhereAI/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java | Updates error log string for sqflite plugin naming. |
| examples/flutter/RunAnywhereAI/.gitignore | Stops ignoring the windows/ folder so Windows runner files are tracked. |
| docs/building.md | Documents Windows ONNX voice validation and Flutter Windows vision setup notes. |
| .gitignore | Ignores .worktrees/ directory. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| rac_result_t rac_extract_archive_native(const char* archive_path, const char* destination_dir, | ||
| const rac_extraction_options_t* options, | ||
| rac_extraction_progress_fn progress_callback, | ||
| void* user_data, rac_extraction_result_t* out_result) { | ||
| (void)archive_path; | ||
| (void)destination_dir; | ||
| (void)options; | ||
| (void)progress_callback; | ||
| (void)user_data; | ||
| if (out_result) { | ||
| *out_result = {0, 0, 0, 0}; | ||
| } | ||
| RAC_LOG_WARNING(kLogTag, | ||
| "Native archive extraction is disabled in this build"); | ||
| return RAC_ERROR_NOT_SUPPORTED; | ||
| } | ||
|
|
||
| rac_bool_t rac_detect_archive_type(const char* file_path, rac_archive_type_t* out_type) { | ||
| (void)file_path; | ||
| if (out_type) { |
There was a problem hiding this comment.
This stub defines rac_extract_archive_native / rac_detect_archive_type, which are also defined in src/infrastructure/extraction/rac_extraction.cpp. If both files are ever compiled into the same target (e.g., when wiring this stub into the Windows build), it will cause duplicate symbol/linker errors. Ensure the build system conditionally compiles either the real libarchive implementation or this stub (e.g., switch sources based on RAC_PLATFORM_WINDOWS / a RAC_ENABLE_NATIVE_EXTRACTION option).
| bypassLocal == null && | ||
| username.isEmpty && | ||
| password.isEmpty) { | ||
| settings = await _loadLegacyGeneralSettings(prefs); |
There was a problem hiding this comment.
Legacy proxy migration in load() is gated on username.isEmpty && password.isEmpty. If a user has legacy prefs set (proxyEnabled/proxyHost/...) but also has stored credentials, this condition prevents _loadLegacyGeneralSettings from running, and the app will treat the proxy as disabled with empty host/port. Consider triggering legacy migration based solely on the absence of the new per-scope preference keys (enabled/scheme/host/port/bypassLocal), regardless of whether credentials exist.
| bypassLocal == null && | |
| username.isEmpty && | |
| password.isEmpty) { | |
| settings = await _loadLegacyGeneralSettings(prefs); | |
| bypassLocal == null) { | |
| final legacySettings = await _loadLegacyGeneralSettings(prefs); | |
| settings = ProxySettings( | |
| enabled: legacySettings.enabled, | |
| scheme: legacySettings.scheme, | |
| host: legacySettings.host, | |
| port: legacySettings.port, | |
| username: username, | |
| password: password, | |
| bypassLocal: legacySettings.bypassLocal, | |
| ); |
| for %%I in ("%~dp0.") do set "SCRIPT_HOME=%%~fI" | ||
| set "ROOT=%SCRIPT_HOME%" | ||
| set "WINDOWS_SCRIPT_DIR=%ROOT%\scripts\windows" | ||
| set "BUILD_DIR=%ROOT%\build-windows-x64" | ||
| set "DIST_DIR=%ROOT%\dist\windows\x64" |
There was a problem hiding this comment.
ROOT is set to the script directory (.../scripts) rather than the runanywhere-commons root. That makes WINDOWS_SCRIPT_DIR resolve to scripts\scripts\windows, and cmake -S "%ROOT%" will point at the wrong CMakeLists.txt (configure will fail). Compute the repo root as the parent of SCRIPT_HOME (or use %~dp0..) and build paths from there.
There was a problem hiding this comment.
Actionable comments posted: 19
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart (2)
80-83:⚠️ Potential issue | 🟡 MinorSkip audio-player initialization when TTS is unsupported.
Line 274 gates the UI, but Line 82 still initializes audio playback on unsupported platforms. Keep the side effects behind the same capability check so unsupported platforms only render the fallback view.
🛡️ Proposed fix
`@override` void initState() { super.initState(); - unawaited(_initializeAudioPlayer()); + if (PlatformCapabilityService.shared.supportsTextToSpeech) { + unawaited(_initializeAudioPlayer()); + } }Also applies to: 274-285
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart` around lines 80 - 83, The initState currently always calls unawaited(_initializeAudioPlayer()), causing audio initialization on platforms that don't support TTS; change initState so it only calls _initializeAudioPlayer() when the same capability check used for the UI fallback is true (the same predicate/method used around the UI branch at the TTS rendering code). Update initState to check the TTS-support predicate (the same boolean or method used in the TTS UI branch) before calling unawaited(_initializeAudioPlayer()), ensuring platforms that render the fallback view do not run audio-player initialization.
123-181:⚠️ Potential issue | 🔴 CriticalSystem TTS path does not load via SDK, causing
_generateSpeech()to fail immediately on theisTTSVoiceLoadedcheck.In
ModelSelectionSheet._onSystemTTSRow()(lines 458–481), selecting system TTS invokesonModelSelected(systemTTSModel)directly without calling any load function. The callback_refreshModelState()detects the system TTS preference and returns early (line 139) after updating local UI state only.Later,
_generateSpeech()(line 178) unconditionally checksif (!sdk.RunAnywhere.isTTSVoiceLoaded)with no bypass for system TTS. Since system TTS selection never calledloadTTSVoice()or equivalent,isTTSVoiceLoadedremains false, causing the error "TTS component not loaded" even though a voice is visibly selected.In contrast, non-system TTS selection (line 524) goes through
_viewModel.selectModel(model), which callsloadTTSVoice(model.id)first, soisTTSVoiceLoadedis true by the time_generateSpeech()runs.Either system TTS must also call a load function before returning from
_refreshModelState(), or_generateSpeech()must handle system TTS as a separate path that does not requireisTTSVoiceLoaded.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart` around lines 123 - 181, Selecting a system TTS model only updates UI state in _refreshModelState (early return when model.preferredFramework == LLMFramework.systemTTS) but never loads the TTS component, so _generateSpeech fails on sdk.RunAnywhere.isTTSVoiceLoaded; fix by loading the system TTS when system is selected (mirror non-system flow) — e.g., in the branch inside _refreshModelState (or in ModelSelectionSheet._onSystemTTSRow) call the same loading path used by _viewModel.selectModel (or invoke the SDK system-TTS load function) so sdk.RunAnywhere.isTTSVoiceLoaded becomes true, or alternatively update _generateSpeech to bypass the isTTSVoiceLoaded check for system TTS by detecting _isSystemTTS; target symbols: _refreshModelState, ModelSelectionSheet._onSystemTTSRow, _viewModel.selectModel, sdk.RunAnywhere.isTTSVoiceLoaded, loadTTSVoice/_viewModel.loadTTSVoice.examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart (1)
88-91:⚠️ Potential issue | 🟡 MinorGuard the init-time permission check with the same capability gate.
Line 267 prevents rendering STT on unsupported platforms, but Line 90 still calls the STT permission service before
build()can return the unsupported view. On platforms where STT permissions/plugins are unavailable, this can fail even though the feature is gated.🛡️ Proposed fix
`@override` void initState() { super.initState(); - unawaited(_checkMicrophonePermission()); + if (PlatformCapabilityService.shared.supportsSpeechToText) { + unawaited(_checkMicrophonePermission()); + } }Also applies to: 267-278
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart` around lines 88 - 91, The initState currently calls _checkMicrophonePermission() unconditionally; guard that call with the same capability check used in build (the STT/platform support gate) so we only request permissions when speech-to-text is available—e.g., in initState, wrap unawaited(_checkMicrophonePermission()) inside the same boolean check used to decide whether to render STT in build; apply the same guarding to the other init-time permission checks referenced around the build/STT gate (lines ~267-278) so no STT plugin calls run on unsupported platforms.sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt (2)
284-297:⚠️ Potential issue | 🟡 MinorDuplicate
elseif(RAC_PLATFORM_WINDOWS)— second branch is unreachable.Same issue as the upper chain: the branch at lines 294-297 is dead (first match at 284-288 wins). The dead branch is only a
message()here so behavior is not affected, but please remove it to avoid confusion / future drift.🩹 Proposed fix
elseif(RAC_PLATFORM_LINUX) message(STATUS "Configuring LlamaCPP backend for Linux") # Linux-specific link libraries target_link_libraries(rac_backend_llamacpp PUBLIC pthread dl) - -elseif(RAC_PLATFORM_WINDOWS) - message(STATUS "Configuring LlamaCPP backend for Windows") - # No extra link libraries needed on Windows (threading is built-in) endif()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt` around lines 284 - 297, Remove the duplicate elseif(RAC_PLATFORM_WINDOWS) branch that appears after the Linux branch; keep the first Windows branch that configures rac_backend_llamacpp (the message(), target_compile_definitions(rac_backend_llamacpp PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN) and target_link_libraries(rac_backend_llamacpp PUBLIC ws2_32)) and delete the second redundant elseif(RAC_PLATFORM_WINDOWS) block (the extra message() and comment) so only one Windows conditional for RAC_PLATFORM_WINDOWS remains.
76-113:⚠️ Potential issue | 🔴 CriticalCritical: duplicate
elseif(RAC_PLATFORM_WINDOWS)— second branch is dead code and drops intended settings.Two Windows branches exist in the same
if/elseifchain. CMake takes the first match (lines 76-85) and silently skips lines 100-113, so:
GGML_OPENMP OFF(line 110) is never applied on Windows, contradicting the second block's clear intent (and the message"...Windows (CPU-only)"). On MSVC this can produce OpenMP link/runtime surprises vs. the other disabled-OpenMP platforms (Android).- The status message on line 112 is never printed.
The AI summary claimed one duplicate was removed, but both are still present — please collapse them into a single branch that preserves
GGML_OPENMP OFFand the status message.🩹 Proposed fix
elseif(RAC_PLATFORM_WINDOWS) set(GGML_METAL OFF CACHE BOOL "" FORCE) set(GGML_VULKAN OFF CACHE BOOL "" FORCE) set(GGML_CUDA OFF CACHE BOOL "" FORCE) set(GGML_OPENCL OFF CACHE BOOL "" FORCE) set(GGML_HIPBLAS OFF CACHE BOOL "" FORCE) set(GGML_SYCL OFF CACHE BOOL "" FORCE) set(GGML_KOMPUTE OFF CACHE BOOL "" FORCE) set(GGML_RPC OFF CACHE BOOL "" FORCE) + set(GGML_OPENMP OFF CACHE BOOL "" FORCE) set(GGML_NATIVE OFF CACHE BOOL "" FORCE) + message(STATUS "Configuring llama.cpp for Windows (CPU-only)") elseif(RAC_PLATFORM_LINUX) ... -elseif(RAC_PLATFORM_WINDOWS) - # Windows: CPU-only by default, no Metal/CUDA/Vulkan - set(GGML_METAL OFF CACHE BOOL "" FORCE) - set(GGML_CUDA OFF CACHE BOOL "" FORCE) - set(GGML_VULKAN OFF CACHE BOOL "" FORCE) - set(GGML_OPENCL OFF CACHE BOOL "" FORCE) - set(GGML_HIPBLAS OFF CACHE BOOL "" FORCE) - set(GGML_SYCL OFF CACHE BOOL "" FORCE) - set(GGML_KOMPUTE OFF CACHE BOOL "" FORCE) - set(GGML_RPC OFF CACHE BOOL "" FORCE) - set(GGML_OPENMP OFF CACHE BOOL "" FORCE) - set(GGML_NATIVE OFF CACHE BOOL "" FORCE) - message(STATUS "Configuring llama.cpp for Windows (CPU-only)") endif()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt` around lines 76 - 113, There are two elseif(RAC_PLATFORM_WINDOWS) branches causing the first to shadow the second and never apply the Windows-specific settings; collapse them into a single elseif(RAC_PLATFORM_WINDOWS) branch that combines all intended settings from both blocks (e.g., keep set(GGML_METAL OFF ...), set(GGML_CUDA OFF ...), set(GGML_VULKAN OFF ...), set(GGML_OPENCL OFF ...), set(GGML_HIPBLAS OFF ...), set(GGML_SYCL OFF ...), set(GGML_KOMPUTE OFF ...), set(GGML_RPC OFF ...), set(GGML_NATIVE OFF ...), and ensure set(GGML_OPENMP OFF CACHE BOOL "" FORCE) is included) and preserve the message(STATUS "Configuring llama.cpp for Windows (CPU-only)"); remove the duplicate elseif to avoid dead code.sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart (1)
226-232:⚠️ Potential issue | 🟡 MinorStale
// ignore: unused_elementannotation.
_authenticateWithBackendis still invoked at Line 162 of this file, so// ignore: unused_elementis misleading and will hide a real signal if the method ever actually becomes unused. Please drop the directive.Proposed fix
/// Authenticate with backend for production/staging environments. /// Matches Swift: CppBridge.Auth.authenticate(apiKey:) in setupHTTP() - // ignore: unused_element static Future<void> _authenticateWithBackend(🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart` around lines 226 - 232, Remove the stale analyzer ignore before the private method: delete the "// ignore: unused_element" annotation immediately above the static Future<void> _authenticateWithBackend(SDKInitParams params, SDKLogger logger) declaration so the analyzer will correctly report if the method becomes unused; ensure no other suppressions are added and run the analyzer to confirm there are no unintended warnings after the change (the method is referenced elsewhere in this file).
🟡 Minor comments (17)
examples/flutter/RunAnywhereAI/lib/features/voice/voice_assistant_view.dart-321-330 (1)
321-330:⚠️ Potential issue | 🟡 MinorDead branch:
supportsVoiceAssistantis alwaystrue.Same concern as
vision_hub_view.dart: this guard is currently unreachable becausePlatformCapabilityService.supportsVoiceAssistantis hardcoded totrue. If the intent (per PR messaging) is that voice features aren't enabled in the Windows vertical-slice build, either wire the capability flag to!Platform.isWindows, or drop the guard until there's a real condition to gate on. Otherwise users on Windows will hit runtime failures further down the pipeline (STT/LLM/TTS availability) rather than this clean fallback.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/voice/voice_assistant_view.dart` around lines 321 - 330, The guard using PlatformCapabilityService.shared.supportsVoiceAssistant is dead because supportsVoiceAssistant is hardcoded true; either make the flag reflect the Windows exclusion by changing PlatformCapabilityService.supportsVoiceAssistant to return !Platform.isWindows (or otherwise check Platform.isWindows where the flag is computed) so the UnsupportedFeatureView path can execute on Windows, or remove this unreachable guard in voice_assistant_view.dart and rely on downstream runtime checks; update any callsites using PlatformCapabilityService.supportsVoiceAssistant and keep PlatformCapabilityService.shared.unsupportedMessage('Voice Assistant') for the fallback messaging if you choose the flag route.examples/flutter/RunAnywhereAI/lib/features/vision/vision_hub_view.dart-19-30 (1)
19-30:⚠️ Potential issue | 🟡 MinorDead branch under current capability config.
PlatformCapabilityService.supportsVisionis hardcoded totrue, so this guard never triggers. If Vision is intended to be unsupported on Windows (consistent with the rest of the PR messaging),supportsVisionshould be!Platform.isWindows(similar tosupportsRag). Otherwise, consider dropping this block until the capability becomes dynamic.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/vision/vision_hub_view.dart` around lines 19 - 30, The guard for vision being unsupported is dead because PlatformCapabilityService.supportsVision is hardcoded true; update the capability check in PlatformCapabilityService (the supportsVision property) to mirror the intended platform rule (e.g., return !Platform.isWindows as done for supportsRag) so the vision branch can trigger on Windows, or remove the unused guard in vision_hub_view.dart if you intend to keep supportsVision always true; locate PlatformCapabilityService.supportsVision (and related uses like capability.unsupportedMessage) to make the change.examples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dart-38-38 (1)
38-38:⚠️ Potential issue | 🟡 MinorReplace deprecated
surfaceVariantwithsurfaceContainerHighest.
ColorScheme.surfaceVariantwas deprecated in Material 3 (v3.18.0+) in favor ofsurfaceContainerHighest. The current code will produce deprecation warnings and break once the symbol is removed. Replace withTheme.of(context).colorScheme.surfaceContainerHighest.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dart` at line 38, Replace the deprecated ColorScheme.surfaceVariant usage with ColorScheme.surfaceContainerHighest: update the code that currently reads Theme.of(context).colorScheme.surfaceVariant to Theme.of(context).colorScheme.surfaceContainerHighest (in the app_colors logic where Theme.of(context).colorScheme is accessed) to remove deprecation warnings and ensure compatibility with Material 3 v3.18.0+.examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart-1-20 (1)
1-20:⚠️ Potential issue | 🟡 Minor
dart:iobreaks Flutter Web; message text is Windows-specific but reused for any unsupported feature.Two concerns:
import 'dart:io'andPlatform.isWindowsare unavailable on Flutter Web. If this app is (or may be) built for web, consumers should usekIsWebfromfoundation.dartbefore touchingPlatform. If web is not a target, this is fine — please confirm.unsupportedMessagehardcodes "…not enabled in the Windows vertical-slice build yet." That text is reused verbatim byvision_hub_view,voice_assistant_view,speech_to_text_view,text_to_speech_view, andrag_demo_view. Today onlysupportsRagcan be false (and only on Windows), so it's accurate in practice — but this will become misleading the moment another flag is flipped tofalseon a non-Windows platform. Consider deriving the platform name dynamically, or taking the platform/reason as a parameter.♻️ Suggested refactor
- String unsupportedMessage(String featureName) => - '$featureName is not enabled in the Windows vertical-slice build yet.'; + String unsupportedMessage(String featureName) { + final platform = Platform.isWindows + ? 'Windows' + : Platform.operatingSystem; + return '$featureName is not enabled in this $platform build yet.'; + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart` around lines 1 - 20, PlatformCapabilityService currently imports dart:io and uses Platform.isWindows (breaking Flutter Web) and returns a hardcoded Windows-specific message in unsupportedMessage; fix by guarding platform checks with kIsWeb from package:flutter/foundation.dart before referencing Platform (or document if web is not a target) and make unsupportedMessage accept an optional platform/reason parameter or derive the platform name dynamically (e.g., pass in a string reason when calling unsupportedMessage) so the message isn’t always Windows-specific; update references to PlatformCapabilityService.supportsRag and unsupportedMessage accordingly.examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_vlm_service.dart-28-40 (1)
28-40:⚠️ Potential issue | 🟡 MinorStream subscription cancellation does not stop the underlying SDK generation.
The
VLMStreamingResultreturned by the SDK includes an explicitcancel()function that callsDartBridge.vlm.cancel()to halt C++ processing. However,processImageStream()only exposesresult.streamto callers and does not retain or expose thecancel()function. When a consumer cancels the stream subscription, it closes the stream but does NOT invoke the SDK's cancel function, leaving the underlying generation running until completion. Callers must explicitly invokecancelGeneration()to stop work—this is not obvious from the stream API.Consider either:
- Exposing the full
VLMStreamingResult(with explicitcancel()access) instead of just the stream- Wiring stream subscription cancellation to call
result.cancel()internally- Adding clear documentation that stream cancellation does not stop generation;
cancelGeneration()must be called explicitly🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_vlm_service.dart` around lines 28 - 40, processImageStream currently yields result.stream but doesn't call the SDK's cancel() when the consumer cancels; update processImageStream to create a StreamController, call sdk.RunAnywhere.processImageStream(...) to get the VLMStreamingResult, add result.stream to the controller (or forward events), and implement onCancel/onPause to invoke result.cancel() so underlying C++ generation is stopped when the Dart subscription is cancelled (keep the public API name processImageStream and ensure any errors from result.stream are forwarded and the controller is closed).examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart-62-68 (1)
62-68:⚠️ Potential issue | 🟡 Minor
_hasCameraDevice = falsepath leaks a prior session.If
initializeCamera()is called a second time andlistDevices()now returns empty (e.g., user unplugged a USB camera, or camera permission was revoked between calls), we set_isCameraInitialized = falseand return, but any previously stored_cameraSessionis not disposed or cleared. Consider disposing it here (same pattern used on line 76) to avoid leaking the underlyingCameraController.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart` around lines 62 - 68, The early-return branch in initializeCamera() sets _hasCameraDevice = false and _isCameraInitialized = false but does not dispose or clear any existing _cameraSession, leaking the previous CameraController; update this branch to mirror the cleanup used on the other path (see the disposal pattern around line 76) by disposing the existing _cameraSession/controller if non-null and then setting _cameraSession (and any related state) to null/cleared before calling _safeNotifyListeners() and returning.examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart-33-44 (1)
33-44:⚠️ Potential issue | 🟡 Minor
device.handle!will crash for any device not produced by this backend.
createSessionforce-unwrapsdevice.handleand casts toCameraDescription. If a caller ever constructs aVisionCameraDevicedirectly (e.g., a different backend, a deserialized device, or a test double driving the real implementation), this throwsTypeError/Null check operator used on a null value. Since the abstraction is explicitly designed to be backend-agnostic, the contract should either (a) requirehandlenon-null and document it, or (b) fail with a clear error.🛡️ Suggested defensive check
VisionCameraSession createSession(VisionCameraDevice device) { - final description = device.handle! as CameraDescription; + final handle = device.handle; + if (handle is! CameraDescription) { + throw ArgumentError( + 'CameraPluginVisionCameraBackend requires a VisionCameraDevice ' + 'produced by listDevices(); received handle of type ' + '${handle.runtimeType}.', + ); + } return _CameraPluginVisionCameraSession( CameraController( - description, + handle, ResolutionPreset.medium, imageFormatGroup: ImageFormatGroup.bgra8888, ), ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart` around lines 33 - 44, createSession currently force-unwraps VisionCameraDevice.handle and casts to CameraDescription which will crash for devices not produced by this backend; update CameraPluginVisionCameraBackend.createSession to validate that device.handle is non-null and is a CameraDescription before casting (e.g., check device.handle != null && device.handle is CameraDescription) and if the check fails throw a clear ArgumentError/StateError with a message referencing VisionCameraDevice.handle and CameraDescription so callers get a descriptive failure; then use the validated CameraDescription to construct the CameraController and _CameraPluginVisionCameraSession.sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cpp-51-54 (1)
51-54:⚠️ Potential issue | 🟡 MinorHardcoded backend version/name will drift from the actual library.
getBackendVersionreturns hardcoded"0.1.5", while iOS and Android hardcode"0.1.4"— the version mismatch demonstrates the drift problem. Neither implementation queries therac_backend_llamacppbackend that the CMakePOST_BUILDcopies next to the plugin. When the bundled llamacpp backend is bumped, the reported version becomes stale.Consider exposing a C symbol from
rac_backend_llamacpp(e.g.rac_backend_llamacpp_version()) and calling it here, or derive the constant from the same source-of-truth the build uses (e.g. a generated header) so all platforms stay in sync.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cpp` around lines 51 - 54, getBackendVersion/getBackendName currently return hardcoded strings that will drift; instead, declare and call an exported C function from the native backend (e.g. rac_backend_llamacpp_version()) or read a generated shared header constant and use its value in the plugin. Concretely: add an extern "C" declaration for rac_backend_llamacpp_version() in runanywhere_llamacpp_plugin.cpp, call it inside the getBackendVersion branch to return its string, and similarly derive getBackendName (or call an exported rac_backend_llamacpp_name() if available); ensure you link against rac_backend_llamacpp so the symbol resolves at runtime and remove the hardcoded literals.sdk/runanywhere-commons/README.md-267-280 (1)
267-280:⚠️ Potential issue | 🟡 MinorFix the Windows ONNX artifact list.
The ONNX build instructions list
rac_backend_llamacpp.dllbut omit the expected ONNX backend output, which can mislead users validatingbuild-windows.bat onnx.📝 Proposed doc fix
Artifacts: - `dist/windows/x64/rac_commons.dll` -- `dist/windows/x64/rac_backend_llamacpp.dll` +- `dist/windows/x64/rac_backend_onnx.dll` +- `dist/windows/x64/rac_backend_llamacpp.dll` when building `llamacpp` or `all`🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/README.md` around lines 267 - 280, Update the Windows ONNX Dependencies section in README.md to include the ONNX backend artifact produced by running "cmd /c scripts\build-windows.bat onnx": add the missing artifact name (e.g., rac_backend_onnx.dll) to the Artifacts list alongside rac_commons.dll and rac_backend_llamacpp.dll and note that it is produced by the "onnx" build target to avoid confusion when validating build-windows.bat onnx.examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart-365-378 (1)
365-378:⚠️ Potential issue | 🟡 MinorAvoid Windows-specific failure text in shared app code.
These catch blocks run in the common Flutter app. On non-Windows platforms, “this Windows build” makes real ONNX/RAG registration failures look platform-specific when they are not.
Proposed wording cleanup
try { await Onnx.register(); - debugPrint('✅ ONNX backend registered (Windows optional)'); + debugPrint('✅ ONNX backend registered'); } catch (e) { - debugPrint('⚠️ ONNX backend not available on this Windows build: $e'); + debugPrint('⚠️ ONNX backend not available on this build: $e'); } @@ try { await RAGModule.register(); debugPrint('✅ RAG backend registered'); } catch (e) { - debugPrint('⚠️ RAG backend not available on this Windows build: $e'); + debugPrint('⚠️ RAG backend not available on this build: $e'); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart` around lines 365 - 378, The catch messages for Onnx.register and RAGModule.register incorrectly state "this Windows build" which is misleading on non-Windows platforms; update the catch handlers for Onnx.register and RAGModule.register to use neutral, platform-agnostic failure messages (e.g., "ONNX backend not available" and "RAG backend not available") and include the caught exception details in the debug output so errors remain informative across all platforms.sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp-150-162 (1)
150-162:⚠️ Potential issue | 🟡 MinorConsider tightening the
mmprojmatch to the llama.cpp convention.
find("mmproj") != nposmatches anywhere in the filename, so a legitimate model whose name happens to contain the substring (e.g. an unusually named weight file, or a future sibling artifact) would be silently skipped. The llama.cpp /gguf_splitconvention is anmmproj-*.ggufprefix (or baremmproj.gguf). A prefix check after stripping directory components is both more precise and easier to reason about.♻️ Suggested tightening
static bool is_auxiliary_model_file(const std::string& filename) { std::string lower(filename); for (auto& c : lower) c = static_cast<char>(tolower(c)); - return lower.find("mmproj") != std::string::npos; + // llama.cpp multimodal packages ship the projector as `mmproj-*.gguf` + // (occasionally bare `mmproj.gguf`). Match the prefix, not any substring. + return lower.rfind("mmproj-", 0) == 0 || lower == "mmproj.gguf"; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp` around lines 150 - 162, The is_auxiliary_model_file function is too permissive by matching "mmproj" anywhere; change it to operate on the filename's basename (strip directory components) and perform a case-insensitive check that only returns true for the llama.cpp conventions: either the exact basename "mmproj.gguf" or a basename that starts with "mmproj-" (e.g., "mmproj-*.gguf"); update is_auxiliary_model_file to extract the basename, normalize case, and then test for equality to "mmproj.gguf" or a prefix "mmproj-" rather than using find anywhere in the string.examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart-234-406 (1)
234-406:⚠️ Potential issue | 🟡 MinorMinor:
TextEditingControllers in the proxy dialog are never disposed.
hostController,portController,usernameController, andpasswordController(lines 236-242) are allocated each time the dialog opens but are never.dispose()-ed — every Configure → close cycle leaks four controllers (and their internal listeners). Dispose them when the dialog future completes:await showDialog<void>( context: context, builder: (dialogContext) => StatefulBuilder( ... ), ); + hostController.dispose(); + portController.dispose(); + usernameController.dispose(); + passwordController.dispose(); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart` around lines 234 - 406, The TextEditingController instances created in _showProxyConfigDialog (hostController, portController, usernameController, passwordController) are never disposed, causing leaks; wrap the showDialog call in a try/finally (or ensure after the await showDialog completes) and call dispose() on each controller in the finally block so they are always cleaned up (dispose hostController, portController, usernameController, passwordController), leaving the existing dialog logic unchanged.examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.cpp-45-66 (1)
45-66:⚠️ Potential issue | 🟡 MinorPotential null-deref on
WM_FONTCHANGEwhenflutter_controller_is null.The guard on Line 50 short-circuits only the
HandleTopLevelWindowProccall, but theswitchon Lines 59-63 unconditionally dereferencesflutter_controller_->engine(). If aWM_FONTCHANGEarrives beforeOnCreatecompletes or afterOnDestroynulls the controller, this crashes.That said, this file is vendored from
flutter_secure_storage_windows's example and mirrors the upstream Flutter Windows runner template — the same latent issue exists upstream. If the intent is strictly to vendor unchanged, leave it alone; otherwise a two-line guard is trivial:Proposed guard (only if patching the vendored copy is acceptable)
switch (message) { case WM_FONTCHANGE: - flutter_controller_->engine()->ReloadSystemFonts(); + if (flutter_controller_ && flutter_controller_->engine()) { + flutter_controller_->engine()->ReloadSystemFonts(); + } break; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.cpp` around lines 45 - 66, The WM_FONTCHANGE handling can dereference flutter_controller_ when it is null; update FlutterWindow::MessageHandler to check flutter_controller_ before calling flutter_controller_->engine()->ReloadSystemFonts() (i.e., only call engine()->ReloadSystemFonts() if flutter_controller_ is non-null), so the switch case for WM_FONTCHANGE is guarded against controller lifetime between OnCreate/OnDestroy; locate the WM_FONTCHANGE case in FlutterWindow::MessageHandler and add the null-check around flutter_controller_ before invoking engine().sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt-35-55 (1)
35-55:⚠️ Potential issue | 🟡 MinorDLL discovery is tightly coupled to the monorepo layout.
Both candidate paths assume the plugin is being built from inside
runanywhere-sdks(the first resolves 4 levels up from the example app; the second 3 levels up from the package). An external Flutter app depending on this plugin via pub won't havesdk/runanywhere-commons/dist/windows/x64/anywhere near those relative locations, andFATAL_ERRORwill block CMake configuration outright.Consider:
- Supporting an override env var / CMake cache var (e.g.,
RAC_COMMONS_DLL_PATHorENV{RAC_COMMONS_DLL}) that takes precedence over the candidate list.- When the plugin is consumed externally, bundling
rac_commons.dllunder the plugin's ownwindows/directory (e.g.,${CMAKE_CURRENT_SOURCE_DIR}/native/rac_commons.dll) as the primary candidate, falling back to the monorepo layout only for in-tree builds.This will also make the error message in Lines 52-54 more actionable for external users, since pointing them at
scripts/build-windows.batonly helps monorepo contributors.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt` around lines 35 - 55, The DLL discovery is too tied to monorepo layout; modify the logic around COMMONS_DLL_CANDIDATES/COMMONS_DLL to first honor an override cache variable or env var (e.g., check a CMake cache var RAC_COMMONS_DLL_PATH or ENV{RAC_COMMONS_DLL} and use it if set), then prefer a plugin-local candidate such as "${CMAKE_CURRENT_SOURCE_DIR}/native/rac_commons.dll" before falling back to the existing repository-relative candidates, and update the fatal message to mention the override option and the plugin-local location instead of only the monorepo build script.docs/building.md-165-170 (1)
165-170:⚠️ Potential issue | 🟡 MinorInclude
rac_commons.dllin the Windows runtime checklist.The Windows build script treats
rac_commons.dllas required and stages it intodist, but this validation list omits it.Proposed docs update
- The final Windows runner directory should contain: + - `rac_commons.dll` - `rac_backend_onnx.dll` - `onnxruntime.dll` - `onnxruntime_providers_shared.dll` - `sherpa-onnx-c-api.dll`🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/building.md` around lines 165 - 170, The Windows runtime checklist in docs (the list containing `rac_backend_onnx.dll`, `onnxruntime.dll`, `onnxruntime_providers_shared.dll`, `sherpa-onnx-c-api.dll`, and companion runtime DLLs) is missing `rac_commons.dll`; update that list to include `rac_commons.dll` so it matches the Windows build script which stages `rac_commons.dll` into `dist`.sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/CMakeLists.txt-55-59 (1)
55-59:⚠️ Potential issue | 🟡 MinorAdd
VERBATIMto the post-build copy command.On Windows, paths with spaces in the checkout directory (e.g.,
C:\Users\...\My Project) can be mishandled by CMake withoutVERBATIM. The keyword ensures proper escaping for the platform's build tool, preventing argument splitting or mangled quotes that would cause the DLL copy to fail.Proposed fix
add_custom_command(TARGET ${PLUGIN_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${LLAMA_BACKEND_DLL}" "$<TARGET_FILE_DIR:${PLUGIN_NAME}>/rac_backend_llamacpp.dll" + VERBATIM )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/CMakeLists.txt` around lines 55 - 59, The post-build add_custom_command for target ${PLUGIN_NAME} may mis-handle paths with spaces; update the add_custom_command invocation that copies "${LLAMA_BACKEND_DLL}" to "$<TARGET_FILE_DIR:${PLUGIN_NAME}>/rac_backend_llamacpp.dll" to include the VERBATIM keyword so CMake will properly escape arguments on Windows (i.e., add VERBATIM to the command invocation for the add_custom_command targeting ${PLUGIN_NAME}).examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.cpp-184-186 (1)
184-186:⚠️ Potential issue | 🟡 MinorIncorrect
STATUS_SUCCESSmapping.
STATUS_SUCCESSis0x00000000, not0xC0000000. The current case is unreachable (BCRYPT_SUCCESS checks already short-circuit success) and the constant itself is wrong, which will mislead anyone diagnosing errors via the returned string. Either drop this case or fix the constant.🔧 Proposed fix
- case 0xc0000000: - oss << " (STATUS_SUCCESS)"; - break; + case 0x00000000: + oss << " (STATUS_SUCCESS)"; + break;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.cpp` around lines 184 - 186, The switch contains an incorrect and unreachable mapping for STATUS_SUCCESS using the value 0xC0000000; update the switch handling (the case labeled "case 0xc0000000") to either be removed or corrected to STATUS_SUCCESS's real value 0x00000000 and adjust the appended string to "(STATUS_SUCCESS)"; note that BCRYPT_SUCCESS paths already short-circuit success, so removing the bogus case is also acceptable—make the change in the same switch block that formats NTSTATUS/BCRYPT return codes in flutter_secure_storage_windows_plugin.cpp.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ca23ca24-4448-4d91-b5ee-51e1ea758286
⛔ Files ignored due to path filters (2)
examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/resources/app_icon.icois excluded by!**/*.icoexamples/flutter/RunAnywhereAI/windows/runner/resources/app_icon.icois excluded by!**/*.ico
📒 Files selected for processing (123)
.gitignoredocs/building.mdexamples/flutter/RunAnywhereAI/.gitignoreexamples/flutter/RunAnywhereAI/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.javaexamples/flutter/RunAnywhereAI/ios/Runner/GeneratedPluginRegistrant.mexamples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dartexamples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dartexamples/flutter/RunAnywhereAI/lib/core/design_system/unsupported_feature_view.dartexamples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dartexamples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dartexamples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dartexamples/flutter/RunAnywhereAI/lib/core/services/proxy_settings_service.dartexamples/flutter/RunAnywhereAI/lib/core/utilities/constants.dartexamples/flutter/RunAnywhereAI/lib/features/chat/chat_interface_view.dartexamples/flutter/RunAnywhereAI/lib/features/models/model_list_view_model.dartexamples/flutter/RunAnywhereAI/lib/features/models/model_selection_sheet.dartexamples/flutter/RunAnywhereAI/lib/features/rag/rag_demo_view.dartexamples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dartexamples/flutter/RunAnywhereAI/lib/features/settings/tool_settings_view_model.dartexamples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dartexamples/flutter/RunAnywhereAI/lib/features/vision/services/vision_permission_gateway.dartexamples/flutter/RunAnywhereAI/lib/features/vision/services/vision_vlm_service.dartexamples/flutter/RunAnywhereAI/lib/features/vision/vision_hub_view.dartexamples/flutter/RunAnywhereAI/lib/features/vision/vlm_camera_view.dartexamples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dartexamples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dartexamples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dartexamples/flutter/RunAnywhereAI/lib/features/voice/voice_assistant_view.dartexamples/flutter/RunAnywhereAI/pubspec.yamlexamples/flutter/RunAnywhereAI/test/features/vision/vlm_view_model_test.dartexamples/flutter/RunAnywhereAI/test/widget_test.dartexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/CHANGELOG.mdexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/LICENSEexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/README.mdexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/analysis_options.yamlexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/README.mdexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/analysis_options.yamlexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/integration_test/app_test.dartexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/lib/main.dartexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/pubspec.yamlexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/CMakeLists.txtexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/CMakeLists.txtexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugin_registrant.ccexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugin_registrant.hexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/flutter/generated_plugins.cmakeexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/CMakeLists.txtexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/Runner.rcexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.cppexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/flutter_window.hexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/main.cppexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/resource.hexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/runner.exe.manifestexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.cppexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/utils.hexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/win32_window.cppexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/windows/runner/win32_window.hexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/flutter_secure_storage_windows.dartexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_ffi.dartexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/lib/src/flutter_secure_storage_windows_stub.dartexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/pubspec.yamlexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/test/unit_test.dartexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/CMakeLists.txtexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.cppexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/flutter_secure_storage_windows_plugin.hexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/windows/include/flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.hexamples/flutter/RunAnywhereAI/windows/.gitignoreexamples/flutter/RunAnywhereAI/windows/CMakeLists.txtexamples/flutter/RunAnywhereAI/windows/flutter/CMakeLists.txtexamples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.ccexamples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.hexamples/flutter/RunAnywhereAI/windows/flutter/generated_plugins.cmakeexamples/flutter/RunAnywhereAI/windows/runner/CMakeLists.txtexamples/flutter/RunAnywhereAI/windows/runner/Runner.rcexamples/flutter/RunAnywhereAI/windows/runner/flutter_window.cppexamples/flutter/RunAnywhereAI/windows/runner/flutter_window.hexamples/flutter/RunAnywhereAI/windows/runner/main.cppexamples/flutter/RunAnywhereAI/windows/runner/resource.hexamples/flutter/RunAnywhereAI/windows/runner/runner.exe.manifestexamples/flutter/RunAnywhereAI/windows/runner/utils.cppexamples/flutter/RunAnywhereAI/windows/runner/utils.hexamples/flutter/RunAnywhereAI/windows/runner/win32_window.cppexamples/flutter/RunAnywhereAI/windows/runner/win32_window.hsdk/runanywhere-commons/CMakeLists.txtsdk/runanywhere-commons/README.mdsdk/runanywhere-commons/scripts/build-windows.batsdk/runanywhere-commons/scripts/windows/download-sherpa-onnx.batsdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txtsdk/runanywhere-commons/src/backends/onnx/CMakeLists.txtsdk/runanywhere-commons/src/backends/onnx/onnx_backend.cppsdk/runanywhere-commons/src/backends/onnx/rac_backend_onnx_register.cppsdk/runanywhere-commons/src/core/rac_core.cppsdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cppsdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction_stub.cppsdk/runanywhere-commons/tests/test_download_orchestrator.cppsdk/runanywhere-flutter/packages/runanywhere/README.mdsdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dartsdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dartsdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_model_registry.dartsdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_stt.dartsdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_tts.dartsdk/runanywhere-flutter/packages/runanywhere/lib/native/platform_loader.dartsdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dartsdk/runanywhere-flutter/packages/runanywhere/pubspec.yamlsdk/runanywhere-flutter/packages/runanywhere/test/vlm_file_resolution_test.dartsdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txtsdk/runanywhere-flutter/packages/runanywhere/windows/include/runanywhere/runanywhere_plugin_c_api.hsdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.cppsdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.hsdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin_c_api.cppsdk/runanywhere-flutter/packages/runanywhere_llamacpp/lib/native/llamacpp_bindings.dartsdk/runanywhere-flutter/packages/runanywhere_llamacpp/pubspec.yamlsdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/CMakeLists.txtsdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/include/runanywhere_llamacpp/runanywhere_llamacpp_plugin_c_api.hsdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cppsdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.hsdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin_c_api.cppsdk/runanywhere-flutter/packages/runanywhere_onnx/lib/native/onnx_bindings.dartsdk/runanywhere-flutter/packages/runanywhere_onnx/pubspec.yamlsdk/runanywhere-flutter/packages/runanywhere_onnx/windows/CMakeLists.txtsdk/runanywhere-flutter/packages/runanywhere_onnx/windows/include/runanywhere_onnx/runanywhere_onnx_plugin_c_api.hsdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.cppsdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.hsdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin_c_api.cpp
💤 Files with no reviewable changes (1)
- examples/flutter/RunAnywhereAI/.gitignore
| final proxyDirective = switch (settings.scheme) { | ||
| ProxyScheme.socks5 => 'SOCKS5 ${settings.host.trim()}:${settings.port}', | ||
| ProxyScheme.http || ProxyScheme.https => | ||
| 'PROXY ${settings.host.trim()}:${settings.port}', | ||
| }; | ||
|
|
||
| client.findProxy = (_) => proxyDirective; |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does dart:io HttpClient.findProxy support SOCKS5 proxy directives?
💡 Result:
No, dart:io HttpClient.findProxy does not support SOCKS5 proxy directives. The findProxy callback returns strings in PAC format, limited to "DIRECT" or "PROXY host:port", which dart:io interprets as HTTP/HTTPS CONNECT proxies only. SOCKS5 is not natively supported. Official Dart API docs confirm findProxy expects "PROXY host:port" format with no mention of SOCKS protocols. findProxyFromEnvironment parses environment variables like http_proxy into this HTTP-only format, defaulting port 1080 but treating as HTTP proxy. Open Dart SDK issue #42920 (since 2020) requests SOCKS support, confirming absence. Users must implement custom connectionFactory for SOCKS5 using third-party packages like socks5_proxy. Native support lacks as of 2026-04-23; recent docs and issues show no change.
Citations:
- 1: https://api.dart.dev/stable/dart-io/HttpClient/findProxyFromEnvironment.html
- 2: https://api.flutter.dev/flutter/dart-io/HttpClient/findProxy.html
- 3: Feature: SOCKS proxies support in HttpClient dart-lang/sdk#42920
- 4: https://api.dart.dev/dart-io/HttpClient/connectionFactory.html
- 5: https://api.dart.dev/stable/latest/dart-io/HttpClient-class.html
- 6: https://api.dart.dev/dart-io/HttpClient/findProxyFromEnvironment.html
- 7: https://api.dart.dev/dart-io/HttpClient/findProxy.html
- 8: https://api.dart.dev/stable/dart-io/HttpClient-class.html
🏁 Script executed:
cat -n examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart | sed -n '70,95p'Repository: RunanywhereAI/runanywhere-sdks
Length of output: 1121
🏁 Script executed:
cat -n examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart | sed -n '65,105p'Repository: RunanywhereAI/runanywhere-sdks
Length of output: 1508
SOCKS5 is not supported by HttpClient.findProxy.
dart:io's HttpClient.findProxy only understands DIRECT and PROXY host:port directives (HTTP CONNECT-style proxies). Returning 'SOCKS5 host:port' from findProxy is not recognized and will result in the client attempting to treat SOCKS5 as an unknown directive — at best falling back in unintended ways, at worst throwing. If SOCKS5 support is required, route the connection via a package like socks5_proxy (e.g. SocksTCPClient.connect / Socks5HttpOverrides) or use a custom connectionFactory rather than relying on findProxy.
Also, the ProxyScheme.https branch returns a plain PROXY host:port, which establishes a plaintext TCP connection to the proxy; if the user intends a TLS connection to the proxy itself, this path does not deliver that. Consider either dropping the https scheme from ProxyScheme or documenting that it is equivalent to http.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart`
around lines 79 - 85, The current client.findProxy handler returns a 'SOCKS5
...' directive and treats ProxyScheme.https as plain 'PROXY', but dart:io
HttpClient.findProxy only supports DIRECT and PROXY host:port; update the logic
in the proxyDirective / client.findProxy handling so that for ProxyScheme.socks5
you do not return a SOCKS5 directive but instead route via a proper SOCKS5
mechanism (e.g., use a Socks5 package or implement a custom
connectionFactory/HttpOverrides) and for ProxyScheme.https either document/clear
that it is equivalent to plaintext PROXY or implement TLS-to-proxy by using a
custom connectionFactory that performs a TLS handshake to the proxy; adjust
references in the code around settings.scheme, ProxyScheme.socks5,
ProxyScheme.http/https and client.findProxy to use the chosen approach.
| onModelSelected: (model) async { | ||
| await _loadModel(model); | ||
| await _refreshModelState(); | ||
| }, | ||
| ), | ||
| )); | ||
| } | ||
|
|
||
| /// Load STT model using RunAnywhere SDK directly (matches Swift STTViewModel pattern) | ||
| Future<void> _loadModel(ModelInfo model) async { | ||
| Future<void> _refreshModelState() async { | ||
| final currentModel = await sdk.RunAnywhere.currentSTTModel(); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify whether ModelSelectionSheet loads STT models before invoking onModelSelected.
set -euo pipefail
fd -i '^model_selection_sheet\.dart$' | while read -r file; do
printf '\n== %s ==\n' "$file"
rg -n -C5 'onModelSelected|loadSTTModel|currentSTTModel|ModelSelectionContext\.stt' "$file" || true
done
printf '\n== Other STT load/current call sites ==\n'
rg -n -C3 '\b(loadSTTModel|currentSTTModel)\s*\(' --type=dart || trueRepository: RunanywhereAI/runanywhere-sdks
Length of output: 7356
🏁 Script executed:
cat -n examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart | sed -n '110,135p'Repository: RunanywhereAI/runanywhere-sdks
Length of output: 1077
🏁 Script executed:
cat -n examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart | grep -A8 -B2 'TTS\|tts\|onModelSelected'Repository: RunanywhereAI/runanywhere-sdks
Length of output: 470
🏁 Script executed:
wc -l examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dartRepository: RunanywhereAI/runanywhere-sdks
Length of output: 152
Load the selected STT model in the callback before refreshing SDK state.
The callback ignores the model parameter and only refreshes the UI state of what's currently loaded. Since _refreshModelState() calls sdk.RunAnywhere.currentSTTModel() without first loading the selected model, the view will continue displaying the previous or null model and transcription will fail.
Add await sdk.RunAnywhere.loadSTTModel(model.id); before await _refreshModelState(); in the onModelSelected callback (lines 116–118).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart`
around lines 116 - 124, In the onModelSelected callback, first call and await
sdk.RunAnywhere.loadSTTModel(model.id) to load the newly selected STT model,
then call await _refreshModelState(); so the UI reads the updated model; update
the onModelSelected handler in the widget to await
sdk.RunAnywhere.loadSTTModel(model.id) before invoking _refreshModelState();
keep the existing _refreshModelState which calls
sdk.RunAnywhere.currentSTTModel().
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart (3)
143-156:⚠️ Potential issue | 🟠 MajorDo not log auth request or response bodies.
Line 144 can log the API key, and Line 156 can log access/refresh tokens. Keep only non-sensitive metadata.
🔒 Proposed log redaction
_logger.debug('Auth POST to: $url'); - _logger.debug('Auth body: $requestJson'); + _logger.debug('Auth request prepared', metadata: { + 'bodyLength': requestJson.length, + }); final response = await http.post( url, headers: { 'Content-Type': 'application/json', @@ ); _logger.debug('Auth response status: ${response.statusCode}'); - _logger.debug('Auth response body: ${response.body}'); + _logger.debug('Auth response received', metadata: { + 'bodyLength': response.body.length, + });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart` around lines 143 - 156, The code currently logs sensitive auth payloads; remove or stop logging the request/response bodies and any raw tokens by deleting the _logger.debug calls that reference requestJson and response.body and avoid printing url if it contains credentials; keep only non-sensitive metadata such as the endpoint path or host and response.statusCode (the existing _logger.debug('Auth response status: ${response.statusCode}') is fine); update references in this block (the variables requestJson, response.body, url and the logger calls) so no API keys, access tokens, refresh tokens, or full JSON bodies are written to logs.
783-805:⚠️ Potential issue | 🟡 MinorWrite UTF-8 bytes into the
Pointer<Utf8>buffer.
value.codeUnitsreturns UTF-16 code units, but the output buffer expects UTF-8 bytes. This causes non-ASCII characters to be corrupted. Thedart:convertimport is available; useutf8.encode(value)instead.🛠️ Proposed encoding fix
if (value == null) return -1; // Copy to output buffer - final bytes = value.codeUnits; + final bytes = utf8.encode(value); final maxLen = bufferSize - 1; // Leave room for null terminator if (bytes.length > maxLen) { return -1; // Buffer too small🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart` around lines 783 - 805, In _secureRetrieveCallback replace the UTF-16 usage with UTF-8: compute bytes with utf8.encode(value) (dart:convert is already available), use that bytes.length when checking against bufferSize and when copying into outValue.cast<Uint8>(), then write the null terminator; this ensures non-ASCII characters are encoded correctly for the Pointer<Utf8> output buffer.
410-435:⚠️ Potential issue | 🟠 MajorUse the configured SDK environment in the native auth request.
Line 429 always sends environment
0, hardcoding development mode regardless of the configured_environmentvalue. This causes staging/production initialization to be ignored forrac_auth_build_authenticate_request.The native environment values are confirmed in
rac_environment.h:
RAC_ENV_DEVELOPMENT = 0RAC_ENV_STAGING = 1RAC_ENV_PRODUCTION = 2Add this mapping function to
DartBridgeAuth:Proposed fix
+ static int _environmentToNativeValue(SDKEnvironment environment) { + switch (environment) { + case SDKEnvironment.development: + return 0; + case SDKEnvironment.staging: + return 1; + case SDKEnvironment.production: + return 2; + } + }Then update line 429:
- config.ref.environment = 0; + config.ref.environment = _environmentToNativeValue(_environment); config.ref.apiKey = apiKeyPtr;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart` around lines 410 - 435, The code in _buildAuthenticateRequestJSON currently hardcodes config.ref.environment = 0 which forces development mode; update DartBridgeAuth to map the instance _environment value to the native rac environment ints (RAC_ENV_DEVELOPMENT=0, RAC_ENV_STAGING=1, RAC_ENV_PRODUCTION=2) and assign that mapped int to config.ref.environment before calling rac_auth_build_authenticate_request; add a small helper method on DartBridgeAuth (e.g., _nativeEnvFor(_environment) or mapEnvironmentToNative) that returns the correct int and use it when setting config.ref.environment on the RacSdkConfigStruct.
🧹 Nitpick comments (1)
examples/flutter/RunAnywhereAI/test/features/dropdown_form_field_compatibility_test.dart (1)
1-38: Exercise dropdown selection, not just initial rendering.These tests would still pass if the dropdown stopped syncing selection state. Please add interaction assertions that open each selector, choose a non-default option, and verify downstream state/callback output.
🧪 Example test expansion
import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:runanywhere_ai/features/models/add_model_from_url_view.dart'; +import 'package:runanywhere_ai/features/models/model_types.dart'; import 'package:runanywhere_ai/features/structured_output/structured_output_view.dart'; void main() { testWidgets('StructuredOutputView builds with example selector', (tester) async { @@ expect(find.byType(StructuredOutputView), findsOneWidget); expect(find.byType(DropdownButtonFormField<int>), findsOneWidget); expect(find.text('Recipe'), findsOneWidget); + + await tester.tap(find.byType(DropdownButtonFormField<int>)); + await tester.pumpAndSettle(); + await tester.tap(find.text('Weather').last); + await tester.pumpAndSettle(); + + final promptField = tester.widget<TextField>(find.byType(TextField)); + expect(promptField.controller?.text, 'What is the weather in Paris?'); }); testWidgets('AddModelFromURLView builds with framework selector', (tester) async { + ModelInfo? addedModel; + await tester.pumpWidget( MaterialApp( home: Scaffold( body: AddModelFromURLView( - onModelAdded: (_) {}, + onModelAdded: (model) => addedModel = model, ), ), ), ); @@ expect(find.byType(AddModelFromURLView), findsOneWidget); expect(find.text('Framework'), findsOneWidget); expect(find.text('Target Framework'), findsOneWidget); + + await tester.tap(find.byType(DropdownButtonFormField<LLMFramework>)); + await tester.pumpAndSettle(); + await tester.tap(find.text(LLMFramework.mediaPipe.displayName).last); + await tester.pumpAndSettle(); + + await tester.enterText(find.byType(TextFormField).at(0), 'Test Model'); + await tester.enterText( + find.byType(TextFormField).at(1), + 'https://example.com/model.gguf', + ); + await tester.tap(find.text('Add Model')); + await tester.pump(); + + expect(addedModel?.preferredFramework, LLMFramework.mediaPipe); + expect(addedModel?.compatibleFrameworks, contains(LLMFramework.mediaPipe)); }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/test/features/dropdown_form_field_compatibility_test.dart` around lines 1 - 38, Update the two widget tests to interact with the dropdowns instead of only asserting initial render: use tester.tap(...) and tester.pumpAndSettle() to open the DropdownButtonFormField<int> inside StructuredOutputView and AddModelFromURLView, tap a non-default DropdownMenuItem label (e.g., a second option like 'Example B' or a different framework string), then pump and assert that the new selection text is visible and any downstream state or callbacks update (for AddModelFromURLView verify the onModelAdded or a visible target framework field changes). If the specific dropdowns are ambiguous, add unique Keys to the DropdownButtonFormField instances in StructuredOutputView and AddModelFromURLView to target them reliably from the tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart`:
- Around line 143-156: The code currently logs sensitive auth payloads; remove
or stop logging the request/response bodies and any raw tokens by deleting the
_logger.debug calls that reference requestJson and response.body and avoid
printing url if it contains credentials; keep only non-sensitive metadata such
as the endpoint path or host and response.statusCode (the existing
_logger.debug('Auth response status: ${response.statusCode}') is fine); update
references in this block (the variables requestJson, response.body, url and the
logger calls) so no API keys, access tokens, refresh tokens, or full JSON bodies
are written to logs.
- Around line 783-805: In _secureRetrieveCallback replace the UTF-16 usage with
UTF-8: compute bytes with utf8.encode(value) (dart:convert is already
available), use that bytes.length when checking against bufferSize and when
copying into outValue.cast<Uint8>(), then write the null terminator; this
ensures non-ASCII characters are encoded correctly for the Pointer<Utf8> output
buffer.
- Around line 410-435: The code in _buildAuthenticateRequestJSON currently
hardcodes config.ref.environment = 0 which forces development mode; update
DartBridgeAuth to map the instance _environment value to the native rac
environment ints (RAC_ENV_DEVELOPMENT=0, RAC_ENV_STAGING=1,
RAC_ENV_PRODUCTION=2) and assign that mapped int to config.ref.environment
before calling rac_auth_build_authenticate_request; add a small helper method on
DartBridgeAuth (e.g., _nativeEnvFor(_environment) or mapEnvironmentToNative)
that returns the correct int and use it when setting config.ref.environment on
the RacSdkConfigStruct.
---
Nitpick comments:
In
`@examples/flutter/RunAnywhereAI/test/features/dropdown_form_field_compatibility_test.dart`:
- Around line 1-38: Update the two widget tests to interact with the dropdowns
instead of only asserting initial render: use tester.tap(...) and
tester.pumpAndSettle() to open the DropdownButtonFormField<int> inside
StructuredOutputView and AddModelFromURLView, tap a non-default DropdownMenuItem
label (e.g., a second option like 'Example B' or a different framework string),
then pump and assert that the new selection text is visible and any downstream
state or callbacks update (for AddModelFromURLView verify the onModelAdded or a
visible target framework field changes). If the specific dropdowns are
ambiguous, add unique Keys to the DropdownButtonFormField instances in
StructuredOutputView and AddModelFromURLView to target them reliably from the
tests.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b2fe9315-691f-41b1-98d8-a8a9a17080f9
📒 Files selected for processing (5)
examples/flutter/RunAnywhereAI/lib/features/models/add_model_from_url_view.dartexamples/flutter/RunAnywhereAI/lib/features/structured_output/structured_output_view.dartexamples/flutter/RunAnywhereAI/test/features/dropdown_form_field_compatibility_test.dartsdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dartsdk/runanywhere-flutter/packages/runanywhere/test/auth_sdk_config_ffi_layout_test.dart
There was a problem hiding this comment.
Actionable comments posted: 10
♻️ Duplicate comments (2)
sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart (1)
238-269:⚠️ Potential issue | 🟠 MajorClose the file sink when streaming fails or cancellation interrupts the loop.
If
response.streamthrows, orclient.close()interrupts the download, execution skipssink.flush()/sink.close(). That can leave partial files open, which is particularly painful on Windows.Ensure the sink is always closed
final file = File(destPath); await file.create(recursive: true); final sink = file.openWrite(); var downloaded = 0; - await for (final chunk in response.stream) { - sink.add(chunk); - downloaded += chunk.length; - - final overallProgress = - _calculateOverallMultiFileDownloadProgress( - completedFiles: i, - totalFiles: totalFiles, - downloadedBytesForCurrentFile: downloaded, - totalBytesForCurrentFile: currentFileTotalBytes, - ); - yield ModelDownloadProgress( - modelId: modelId, - bytesDownloaded: cumulativeDownloaded + downloaded, - totalBytes: totalModelBytes, - stage: ModelDownloadStage.downloading, - overallProgress: overallProgress * 0.9, - ); + try { + await for (final chunk in response.stream) { + sink.add(chunk); + downloaded += chunk.length; + + final overallProgress = + _calculateOverallMultiFileDownloadProgress( + completedFiles: i, + totalFiles: totalFiles, + downloadedBytesForCurrentFile: downloaded, + totalBytesForCurrentFile: currentFileTotalBytes, + ); + yield ModelDownloadProgress( + modelId: modelId, + bytesDownloaded: cumulativeDownloaded + downloaded, + totalBytes: totalModelBytes, + stage: ModelDownloadStage.downloading, + overallProgress: overallProgress * 0.9, + ); + } + + await sink.flush(); + } finally { + await sink.close(); } - - await sink.flush(); - await sink.close(); cumulativeDownloaded += downloaded; _logger.info('Downloaded: ${descriptor.destinationPath}'); } finally {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart` around lines 238 - 269, The file sink opened with File(destPath).openWrite() can remain unclosed if response.stream throws or cancellation occurs; modify the download routine so the sink is always closed: wrap the streaming loop and subsequent flush in a try/finally that guarantees await sink.flush() and await sink.close() (or use sink.close() in the finally) after the await for (final chunk in response.stream) block; ensure this applies around the sink variable created in the method (the openWrite() call assigned to sink) so that even when client.close() or response.stream throws, the sink is closed before removing entries from _activeDownloads or returning from the method.examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart (1)
21-24:⚠️ Potential issue | 🟠 MajorDon’t advertise HTTPS proxy support unless TLS-to-proxy is implemented.
ProxyScheme.httpsremains insupportedValues, butExampleHttpService._proxyDirectiveForSettingsmaps it to the same plainPROXY host:portdirective as HTTP. Users selecting HTTPS will not get a TLS connection to the proxy itself; either remove HTTPS from supported schemes for now or implement a custom connection path that actually negotiates TLS with the proxy.🐛 Minimal fix if HTTPS proxy transport is not supported yet
- bool get isSupportedInExampleApp => this != ProxyScheme.socks5; + bool get isSupportedInExampleApp => this == ProxyScheme.http;Please verify the Dart proxy directive semantics:
Does dart:io HttpClient.findProxy support HTTPS proxy transport, or only DIRECT and PROXY host:port PAC directives?🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart` around lines 21 - 24, The enum value ProxyScheme.https is being advertised as supported but ExampleHttpService._proxyDirectiveForSettings does not establish TLS-to-proxy; either remove HTTPS from the advertised set or implement TLS proxy transport. Fix option A (preferred short-term): change the isSupportedInExampleApp/supportedValues logic to explicitly exclude ProxyScheme.https so HTTPS is not listed as supported. Fix option B (long-term): implement proper TLS-to-proxy handling in ExampleHttpService._proxyDirectiveForSettings so when ProxyScheme.https is chosen you perform a TLS handshake/CONNECT to the proxy rather than emitting a plain "PROXY host:port" directive. Reference symbols: ProxyScheme, isSupportedInExampleApp, supportedValues, and ExampleHttpService._proxyDirectiveForSettings.
🧹 Nitpick comments (5)
sdk/runanywhere-flutter/packages/runanywhere/test/dart_bridge_auth_test.dart (1)
60-61: Redaction assertions are weak — consider checking substring presence across string values.
Iterable.containsperforms equality matching, so these expectations only fail if a metadata value is exactly equal to the fullrequestJson/response.body. Since the metadata map only holds host/path/lengths/status, these checks trivially pass even if a future change started leaking a prefix or substring of the body. A stricter assertion would iterate string values and check that none contain sensitive tokens.🔧 Suggested stronger redaction check
- expect(requestMetadata.values, isNot(contains(requestJson))); - expect(responseMetadata.values, isNot(contains(response.body))); + for (final v in requestMetadata.values.whereType<String>()) { + expect(v.contains('secret-key'), isFalse); + expect(v.contains('device-123'), isFalse); + } + for (final v in responseMetadata.values.whereType<String>()) { + expect(v.contains('secret-token'), isFalse); + expect(v.contains('another-secret'), isFalse); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/test/dart_bridge_auth_test.dart` around lines 60 - 61, The current assertions use Iterable.contains which only checks full equality and can miss partial leaks; update the tests that reference requestMetadata, responseMetadata, requestJson and response.body to assert that no metadata string value contains those payloads as substrings (e.g., iterate over requestMetadata.values/responseMetadata.values and assert none.any/none where the value.contains(requestJson) or value.contains(response.body)). Locate the expectations around expect(requestMetadata.values, ...) and expect(responseMetadata.values, ...) and replace them with substring-based checks so any partial leakage of requestJson or response.body fails the test.examples/flutter/RunAnywhereAI/pubspec.yaml (2)
52-53: Exact pin oncamera_windows— intentional, but consider a caret constraint.
camera_windows: 0.2.6+1is an exact pin, which prevents picking up patch fixes. The adjacent comment clarifies the Dart-3.3 compatibility intent, which is fine; if you want non-breaking patches while still holding the line on Dart compat,^0.2.6(pre-1.0 semantics in pub: patch updates only within0.2.x) would be a reasonable alternative. Not a blocker.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/pubspec.yaml` around lines 52 - 53, The pubspec currently pins camera_windows exactly to "0.2.6+1" which blocks receiving patch fixes; change the dependency entry for camera_windows in pubspec.yaml from the exact pin to a caret constraint like "^0.2.6" to allow non-breaking patch updates while preserving Dart 3.3 compatibility. Locate the camera_windows line and update the version string accordingly, then run pub get to verify dependency resolution.
76-77: Override is redundant with the direct path dependency.Since
flutter_secure_storage_windowson lines 36-37 already points at the vendored path, the matching entry independency_overridesis only strictly needed if some transitive dependency (e.g.,flutter_secure_storage9.x) pulls a different version of this package. If that's the intent (likely, given the comment on line 68), the entry is correct — but worth a brief inline comment explaining it's there to win over the transitive pin fromflutter_secure_storage, not to override the direct declaration.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/pubspec.yaml` around lines 76 - 77, The dependency_overrides entry for flutter_secure_storage_windows is redundant with the direct path dependency unless it's intended to beat a transitive pin from flutter_secure_storage; update the pubspec.yaml to add a brief inline comment next to the dependency_overrides entry explaining that flutter_secure_storage_windows is being overridden to win over a transitive pin from the flutter_secure_storage package (e.g., to force the vendored path), and reference the flutter_secure_storage_windows and dependency_overrides identifiers so future readers understand the purpose.sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart (1)
155-164: Document that factory-created clients are owned and closed here.
downloadModelcloses each returned client later, so callers must not return a shared/singleton client from this factory. A short ownership contract avoids accidental proxy/client reuse breakage.Clarify factory ownership
+ /// Configures the HTTP client factory used for model downloads. + /// + /// The returned client is owned by [ModelDownloadService] and will be closed + /// after the file download completes. Factories should return a fresh client + /// per invocation, not a shared singleton. void configureClientFactory(DownloadHttpClientFactory? factory) { _clientFactory = factory; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart` around lines 155 - 164, Update the public API docs to state that any http.Client returned by the configured factory is owned and will be closed by this class (the DownloadService) when done; specifically document this ownership contract on configureClientFactory (and on the factory typedef if present) so implementers of the factory passed into configureClientFactory and users of _createClient know not to return shared/singleton clients (downloadModel will close each returned client). Ensure the doc string mentions that the service will close the client and callers must return a fresh/disposable client for each call.sdk/runanywhere-flutter/packages/runanywhere/test/download_service_test.dart (1)
29-44: Add one assertion for rounded per-file estimates.The helper in
sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart:19-24usesceil(), so a non-even division case would prevent regressions to floor/truncating behavior.Test coverage addition
test('estimates per-file size from total model size when needed', () { expect( estimatePerFileDownloadSizeForTesting( totalModelBytes: 300, totalFiles: 3, ), 100, ); + expect( + estimatePerFileDownloadSizeForTesting( + totalModelBytes: 301, + totalFiles: 3, + ), + 101, + ); expect( estimatePerFileDownloadSizeForTesting( totalModelBytes: null, totalFiles: 3, ),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/test/download_service_test.dart` around lines 29 - 44, Add a test asserting that estimatePerFileDownloadSizeForTesting uses ceiling for non-even divisions: call estimatePerFileDownloadSizeForTesting(totalModelBytes: 301, totalFiles: 3) and expect 101 to catch any regressions to floor/truncate; update the test block that already checks 300/3 and null case to include this extra expect (the helper uses ceil() in download_service.dart via estimatePerFileDownloadSizeForTesting).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart`:
- Around line 27-55: The proxied request operations in getJson and testProxy can
hang after TCP connect; update both getJson and testProxy (which use
createScopedHttpClient, request.getUrl, request.close, and
response.transform/join) to apply end-to-end timeouts: wrap the request.close()
Future (and in getJson also the body read response.transform(...).join()) with
.timeout(a reasonable Duration) and handle TimeoutException to return a failed
ExampleHttpResponse or false respectively, ensuring client.close(force: true)
still runs in the finally block; use a shared Duration constant for the timeout
to keep behavior consistent across both methods.
In
`@examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart`:
- Around line 1125-1133: The Test button currently always calls
_testProxyConfiguration(scope) which can show "proxy test succeeded" even when
the proxy scope is disabled or incomplete because
ExampleHttpService.createScopedHttpClient falls back to DIRECT; update the UI
and handler so the button is disabled (onPressed == null) when the provided
scope is disabled or missing required fields (e.g., check scope.enabled or
scope.isEnabled and required host/port/auth fields), and/or make
_testProxyConfiguration(scope) return early with an appropriate message if
scope.enabled is false or validation fails; locate the button widget and the
_testProxyConfiguration function and add the guard/validation to prevent running
the test when the proxy is not actually configured.
- Around line 240-416: The TextEditingController instances created in
_showProxyConfigDialog (hostController, portController, usernameController,
passwordController) are not disposed and leak; wrap the await showDialog(...)
call in a try/finally inside _showProxyConfigDialog so that in the finally block
you call dispose() on each controller (hostController.dispose(),
portController.dispose(), usernameController.dispose(),
passwordController.dispose()) to ensure they are always cleaned up after the
dialog closes or an error occurs.
In `@examples/flutter/RunAnywhereAI/pubspec.yaml`:
- Around line 35-37: The pubspec lists flutter_secure_storage: ^9.0.0 but uses a
vendored flutter_secure_storage_windows (v3.1.2) that requires
flutter_secure_storage_platform_interface ^1.1.1, causing a platform_interface
version conflict; fix by either (preferred) updating the vendored
flutter_secure_storage_windows to a 4.x+ release that supports
flutter_secure_storage_platform_interface ^2.0.0 (replace the vendor folder or
point the dependency to a compatible 4.x source), or remove the vendored
flutter_secure_storage_windows override so pub can resolve the transitive
4.x-compatible Windows package, or alternatively downgrade
flutter_secure_storage to a 8.x release that matches platform_interface
^1.1.1—apply the chosen change in pubspec.yaml and run flutter pub get to verify
resolution.
In `@examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp`:
- Around line 147-149: OnCreate() can fail but the code currently returns false
without destroying the HWND created earlier; update the Create() flow so that
after calling UpdateTheme(window) you call OnCreate() and if it returns false
you call ::DestroyWindow(window) (and clear the stored HWND/member window
handle) before returning false, ensuring the native window is torn down when
OnCreate() fails.
- Around line 224-231: The Destroy method currently ignores DestroyWindow's BOOL
result and unconditionally clears window_handle_ and potentially unregisters the
class; update Win32Window::Destroy to capture the return value of
DestroyWindow(window_handle_), only set window_handle_ to nullptr if
DestroyWindow succeeds, and only decrement/consider g_active_window_count and
call WindowClassRegistrar::GetInstance()->UnregisterWindowClass() when
destruction succeeded; if DestroyWindow fails, preserve window_handle_ (so
callers can retry or cleanup later) and surface/log the failure (e.g., via
GetLastError) rather than proceeding to unregister the class.
- Around line 181-221: The switch falls through to DefWindowProc using the
cleared member window_handle_; change the final call to use the incoming hwnd
parameter (call DefWindowProc(hwnd, message, wparam, lparam)) and add a
WM_NCDESTROY case that clears the per-window user data (SetWindowLongPtr(hwnd,
GWLP_USERDATA, 0)) and performs any final cleanup after WM_DESTROY (e.g., ensure
window_handle_ is already nulled and return 0), so default processing gets the
correct hwnd and GWLP_USERDATA is cleared when the non-client destroy message
arrives.
In `@sdk/runanywhere-commons/scripts/build-windows.bat`:
- Around line 100-106: The current Windows batch loop that copies Sherpa ONNX
DLLs (the block gated by BUILD_ONNX) swallows copy failures by redirecting
output to nul; change it so each copy checks the copy exit status (ERRORLEVEL)
after the copy of each file from
"%ROOT%\third_party\sherpa-onnx-windows\bin\*.dll" and
"%ROOT%\third_party\sherpa-onnx-windows\lib\*.dll" and, on failure, emit an
error to the console and exit the script with non‑zero status (e.g., exit /b 1)
so the build fails instead of reporting “Build complete” with missing DLLs.
Ensure you apply this check inside both FOR loops that perform the copy to
"%DIST_DIR%\".
- Around line 8-16: The argument parsing currently silently ignores unknown
options, so update the parse logic around the if "%~1" checks (the parse_args
loop / parse_args label) to reject unrecognized arguments: when none of the
known branches (all, llamacpp, onnx, --clean, -h, --help) match, print a clear
error mentioning the bad token and call the existing usage flow (goto usage) or
exit /b 1; ensure variables like BACKENDS and CLEAN are only set when recognized
and that the parser halts with non-zero status for unknown options to prevent
accidental successful builds with wrong defaults.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart`:
- Around line 203-215: The multi-file download currently treats empty or
partially missing descriptors as success; before calling _updateModelLocalPath
or emitting downloadCompleted, validate the manifest and abort on invalid data:
if multiFile.files.isEmpty, log an error via _logger.error and emit a failure
progress (e.g. ModelDownloadProgress.failed or throw) and return; likewise
inside the loop if descriptor.url == null, immediately log the error, emit a
failure and stop the download rather than continue to the next file. Ensure
neither _updateModelLocalPath nor the final downloadCompleted progress are
called when the manifest is invalid.
---
Duplicate comments:
In `@examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart`:
- Around line 21-24: The enum value ProxyScheme.https is being advertised as
supported but ExampleHttpService._proxyDirectiveForSettings does not establish
TLS-to-proxy; either remove HTTPS from the advertised set or implement TLS proxy
transport. Fix option A (preferred short-term): change the
isSupportedInExampleApp/supportedValues logic to explicitly exclude
ProxyScheme.https so HTTPS is not listed as supported. Fix option B (long-term):
implement proper TLS-to-proxy handling in
ExampleHttpService._proxyDirectiveForSettings so when ProxyScheme.https is
chosen you perform a TLS handshake/CONNECT to the proxy rather than emitting a
plain "PROXY host:port" directive. Reference symbols: ProxyScheme,
isSupportedInExampleApp, supportedValues, and
ExampleHttpService._proxyDirectiveForSettings.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart`:
- Around line 238-269: The file sink opened with File(destPath).openWrite() can
remain unclosed if response.stream throws or cancellation occurs; modify the
download routine so the sink is always closed: wrap the streaming loop and
subsequent flush in a try/finally that guarantees await sink.flush() and await
sink.close() (or use sink.close() in the finally) after the await for (final
chunk in response.stream) block; ensure this applies around the sink variable
created in the method (the openWrite() call assigned to sink) so that even when
client.close() or response.stream throws, the sink is closed before removing
entries from _activeDownloads or returning from the method.
---
Nitpick comments:
In `@examples/flutter/RunAnywhereAI/pubspec.yaml`:
- Around line 52-53: The pubspec currently pins camera_windows exactly to
"0.2.6+1" which blocks receiving patch fixes; change the dependency entry for
camera_windows in pubspec.yaml from the exact pin to a caret constraint like
"^0.2.6" to allow non-breaking patch updates while preserving Dart 3.3
compatibility. Locate the camera_windows line and update the version string
accordingly, then run pub get to verify dependency resolution.
- Around line 76-77: The dependency_overrides entry for
flutter_secure_storage_windows is redundant with the direct path dependency
unless it's intended to beat a transitive pin from flutter_secure_storage;
update the pubspec.yaml to add a brief inline comment next to the
dependency_overrides entry explaining that flutter_secure_storage_windows is
being overridden to win over a transitive pin from the flutter_secure_storage
package (e.g., to force the vendored path), and reference the
flutter_secure_storage_windows and dependency_overrides identifiers so future
readers understand the purpose.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart`:
- Around line 155-164: Update the public API docs to state that any http.Client
returned by the configured factory is owned and will be closed by this class
(the DownloadService) when done; specifically document this ownership contract
on configureClientFactory (and on the factory typedef if present) so
implementers of the factory passed into configureClientFactory and users of
_createClient know not to return shared/singleton clients (downloadModel will
close each returned client). Ensure the doc string mentions that the service
will close the client and callers must return a fresh/disposable client for each
call.
In
`@sdk/runanywhere-flutter/packages/runanywhere/test/dart_bridge_auth_test.dart`:
- Around line 60-61: The current assertions use Iterable.contains which only
checks full equality and can miss partial leaks; update the tests that reference
requestMetadata, responseMetadata, requestJson and response.body to assert that
no metadata string value contains those payloads as substrings (e.g., iterate
over requestMetadata.values/responseMetadata.values and assert none.any/none
where the value.contains(requestJson) or value.contains(response.body)). Locate
the expectations around expect(requestMetadata.values, ...) and
expect(responseMetadata.values, ...) and replace them with substring-based
checks so any partial leakage of requestJson or response.body fails the test.
In
`@sdk/runanywhere-flutter/packages/runanywhere/test/download_service_test.dart`:
- Around line 29-44: Add a test asserting that
estimatePerFileDownloadSizeForTesting uses ceiling for non-even divisions: call
estimatePerFileDownloadSizeForTesting(totalModelBytes: 301, totalFiles: 3) and
expect 101 to catch any regressions to floor/truncate; update the test block
that already checks 300/3 and null case to include this extra expect (the helper
uses ceil() in download_service.dart via estimatePerFileDownloadSizeForTesting).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: eb9c1fcc-a63e-48b0-92dd-826067395d11
📒 Files selected for processing (20)
examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dartexamples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dartexamples/flutter/RunAnywhereAI/lib/core/services/proxy_settings_service.dartexamples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dartexamples/flutter/RunAnywhereAI/pubspec.yamlexamples/flutter/RunAnywhereAI/test/core/models/proxy_settings_test.dartexamples/flutter/RunAnywhereAI/test/core/services/example_http_service_test.dartexamples/flutter/RunAnywhereAI/test/core/services/proxy_settings_service_test.dartexamples/flutter/RunAnywhereAI/test/features/dropdown_form_field_compatibility_test.dartexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/pubspec.yamlexamples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/pubspec.yamlexamples/flutter/RunAnywhereAI/windows/runner/flutter_window.cppexamples/flutter/RunAnywhereAI/windows/runner/win32_window.cppsdk/runanywhere-commons/scripts/build-windows.batsdk/runanywhere-commons/src/backends/onnx/rac_backend_onnx_register.cppsdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dartsdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dartsdk/runanywhere-flutter/packages/runanywhere/lib/native/platform_loader.dartsdk/runanywhere-flutter/packages/runanywhere/test/dart_bridge_auth_test.dartsdk/runanywhere-flutter/packages/runanywhere/test/download_service_test.dart
✅ Files skipped from review due to trivial changes (3)
- examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/pubspec.yaml
- examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/example/pubspec.yaml
- sdk/runanywhere-commons/src/backends/onnx/rac_backend_onnx_register.cpp
🚧 Files skipped from review as they are similar to previous changes (4)
- sdk/runanywhere-flutter/packages/runanywhere/lib/native/platform_loader.dart
- examples/flutter/RunAnywhereAI/windows/runner/flutter_window.cpp
- examples/flutter/RunAnywhereAI/test/features/dropdown_form_field_compatibility_test.dart
- sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart
| Future<ExampleHttpResponse> getJson(Uri uri) async { | ||
| final client = await createScopedHttpClient(ProxyScope.general, uri); | ||
|
|
||
| try { | ||
| final request = await client.getUrl(uri); | ||
| request.headers.set(HttpHeaders.acceptHeader, 'application/json'); | ||
|
|
||
| final response = await request.close(); | ||
| final body = await response.transform(utf8.decoder).join(); | ||
|
|
||
| return ExampleHttpResponse( | ||
| statusCode: response.statusCode, | ||
| body: body, | ||
| ); | ||
| } finally { | ||
| client.close(force: true); | ||
| } | ||
| } | ||
|
|
||
| Future<bool> testProxy(ProxyScope scope, Uri uri) async { | ||
| final client = await createScopedHttpClient(scope, uri); | ||
| try { | ||
| final request = await client.getUrl(uri); | ||
| request.headers.set(HttpHeaders.acceptHeader, 'application/json'); | ||
| final response = await request.close(); | ||
| return response.statusCode >= 200 && response.statusCode < 300; | ||
| } finally { | ||
| client.close(force: true); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does Dart HttpClient.connectionTimeout cover only TCP connection establishment, or does it also timeout request.close() and response body reads?
💡 Result:
Dart's HttpClient.connectionTimeout covers only the TCP connection establishment phase (specifically, when connecting to a new host). If the timeout is exceeded, a SocketException is thrown. It does not timeout request.close or response body reads. For timeouts on other phases: - idleTimeout applies to non-active persistent (keep-alive) connections (default 15 seconds). - Timeouts for sending the request (request.close), receiving headers, or reading the response body must be implemented manually using Future.timeout on those operations or by calling request.abort on timeout. There are no built-in HttpClient properties for those phases.
Citations:
- 1: https://api.flutter.dev/flutter/dart-io/HttpClient/connectionTimeout.html
- 2: https://api.dart.dev/stable/2.18.5/dart-io/HttpClient/connectionTimeout.html
- 3: https://api.dart.dev/stable/2.18.4/dart-io/HttpClient/idleTimeout.html
- 4: https://api.flutter.dev/flutter/dart-io/HttpClient/idleTimeout.html
- 5: https://api.dart.dev/stable/latest/dart-io/HttpClient-class.html
Add an end-to-end timeout for proxied requests.
connectionTimeout only bounds TCP connection establishment; request.close() and body reads can still hang on a stalled proxy/server, which blocks tool HTTP calls and proxy tests indefinitely. Manual timeout handling via .timeout() is required for these operations.
🛡️ Proposed fix
class ExampleHttpService {
static final ExampleHttpService shared = ExampleHttpService._internal();
+ static const Duration _requestTimeout = Duration(seconds: 30);
@@
- final request = await client.getUrl(uri);
+ final request = await client.getUrl(uri).timeout(_requestTimeout);
request.headers.set(HttpHeaders.acceptHeader, 'application/json');
- final response = await request.close();
- final body = await response.transform(utf8.decoder).join();
+ final response = await request.close().timeout(_requestTimeout);
+ final body =
+ await response.transform(utf8.decoder).join().timeout(_requestTimeout);
@@
- final request = await client.getUrl(uri);
+ final request = await client.getUrl(uri).timeout(_requestTimeout);
request.headers.set(HttpHeaders.acceptHeader, 'application/json');
- final response = await request.close();
+ final response = await request.close().timeout(_requestTimeout);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart`
around lines 27 - 55, The proxied request operations in getJson and testProxy
can hang after TCP connect; update both getJson and testProxy (which use
createScopedHttpClient, request.getUrl, request.close, and
response.transform/join) to apply end-to-end timeouts: wrap the request.close()
Future (and in getJson also the body read response.transform(...).join()) with
.timeout(a reasonable Duration) and handle TimeoutException to return a failed
ExampleHttpResponse or false respectively, ensuring client.close(force: true)
still runs in the finally block; use a shared Duration constant for the timeout
to keep behavior consistent across both methods.
| Future<void> _showProxyConfigDialog(ProxyScope scope) async { | ||
| final currentSettings = await ProxySettingsService.shared.load(scope); | ||
| final hostController = TextEditingController(text: currentSettings.host); | ||
| final portController = | ||
| TextEditingController(text: currentSettings.port?.toString() ?? ''); | ||
| final usernameController = | ||
| TextEditingController(text: currentSettings.username); | ||
| final passwordController = | ||
| TextEditingController(text: currentSettings.password); | ||
| var proxyEnabled = currentSettings.enabled && | ||
| currentSettings.scheme.isSupportedInExampleApp; | ||
| var selectedScheme = currentSettings.scheme.isSupportedInExampleApp | ||
| ? currentSettings.scheme | ||
| : ProxyScheme.http; | ||
| var bypassLocal = currentSettings.bypassLocal; | ||
| var showPassword = false; | ||
|
|
||
| if (!mounted) return; | ||
|
|
||
| await showDialog<void>( | ||
| context: context, | ||
| builder: (dialogContext) => StatefulBuilder( | ||
| builder: (dialogContext, setDialogState) => AlertDialog( | ||
| title: Text(scope.displayName), | ||
| content: SingleChildScrollView( | ||
| child: Column( | ||
| mainAxisSize: MainAxisSize.min, | ||
| crossAxisAlignment: CrossAxisAlignment.start, | ||
| children: [ | ||
| SwitchListTile( | ||
| contentPadding: EdgeInsets.zero, | ||
| title: const Text('Enable Proxy'), | ||
| value: proxyEnabled, | ||
| onChanged: (value) { | ||
| setDialogState(() { | ||
| proxyEnabled = value; | ||
| }); | ||
| }, | ||
| ), | ||
| const SizedBox(height: AppSpacing.smallMedium), | ||
| DropdownButtonFormField<ProxyScheme>( | ||
| value: selectedScheme, | ||
| decoration: const InputDecoration( | ||
| labelText: 'Protocol', | ||
| border: OutlineInputBorder(), | ||
| ), | ||
| items: ProxyScheme.supportedValues | ||
| .map( | ||
| (scheme) => DropdownMenuItem( | ||
| value: scheme, | ||
| child: Text(scheme.displayName), | ||
| ), | ||
| ) | ||
| .toList(), | ||
| onChanged: (value) { | ||
| if (value != null) { | ||
| setDialogState(() { | ||
| selectedScheme = value; | ||
| }); | ||
| } | ||
| }, | ||
| ), | ||
| const SizedBox(height: AppSpacing.mediumLarge), | ||
| TextField( | ||
| controller: hostController, | ||
| decoration: const InputDecoration( | ||
| labelText: 'Host', | ||
| hintText: '127.0.0.1', | ||
| border: OutlineInputBorder(), | ||
| ), | ||
| ), | ||
| const SizedBox(height: AppSpacing.mediumLarge), | ||
| TextField( | ||
| controller: portController, | ||
| keyboardType: TextInputType.number, | ||
| decoration: const InputDecoration( | ||
| labelText: 'Port', | ||
| hintText: '7890', | ||
| border: OutlineInputBorder(), | ||
| ), | ||
| ), | ||
| const SizedBox(height: AppSpacing.mediumLarge), | ||
| TextField( | ||
| controller: usernameController, | ||
| decoration: const InputDecoration( | ||
| labelText: 'Username', | ||
| border: OutlineInputBorder(), | ||
| ), | ||
| ), | ||
| const SizedBox(height: AppSpacing.mediumLarge), | ||
| TextField( | ||
| controller: passwordController, | ||
| obscureText: !showPassword, | ||
| decoration: InputDecoration( | ||
| labelText: 'Password', | ||
| border: const OutlineInputBorder(), | ||
| suffixIcon: IconButton( | ||
| icon: Icon( | ||
| showPassword ? Icons.visibility_off : Icons.visibility, | ||
| ), | ||
| onPressed: () { | ||
| setDialogState(() { | ||
| showPassword = !showPassword; | ||
| }); | ||
| }, | ||
| ), | ||
| ), | ||
| ), | ||
| const SizedBox(height: AppSpacing.mediumLarge), | ||
| SwitchListTile( | ||
| contentPadding: EdgeInsets.zero, | ||
| title: const Text('Bypass localhost'), | ||
| subtitle: const Text( | ||
| 'Skip proxy for localhost, 127.0.0.1, and ::1'), | ||
| value: bypassLocal, | ||
| onChanged: (value) { | ||
| setDialogState(() { | ||
| bypassLocal = value; | ||
| }); | ||
| }, | ||
| ), | ||
| ], | ||
| ), | ||
| ), | ||
| actions: [ | ||
| TextButton( | ||
| onPressed: () => Navigator.pop(dialogContext), | ||
| child: const Text('Cancel'), | ||
| ), | ||
| TextButton( | ||
| onPressed: () async { | ||
| final settings = ProxySettings( | ||
| enabled: proxyEnabled, | ||
| scheme: selectedScheme, | ||
| host: hostController.text.trim(), | ||
| port: int.tryParse(portController.text.trim()), | ||
| username: usernameController.text.trim(), | ||
| password: passwordController.text, | ||
| bypassLocal: bypassLocal, | ||
| ); | ||
|
|
||
| final result = | ||
| await ProxySettingsService.shared.save(scope, settings); | ||
| if (!result.isValid) { | ||
| if (mounted) { | ||
| ScaffoldMessenger.of(context).showSnackBar( | ||
| SnackBar( | ||
| content: Text( | ||
| result.message ?? 'Invalid proxy settings', | ||
| ), | ||
| ), | ||
| ); | ||
| } | ||
| return; | ||
| } | ||
|
|
||
| await _loadProxyConfiguration(scope); | ||
|
|
||
| if (mounted) { | ||
| ScaffoldMessenger.of(context).showSnackBar( | ||
| SnackBar( | ||
| content: Text('${scope.displayName} settings saved'), | ||
| ), | ||
| ); | ||
| } | ||
|
|
||
| if (dialogContext.mounted) { | ||
| Navigator.pop(dialogContext); | ||
| } | ||
| }, | ||
| child: const Text('Save'), | ||
| ), | ||
| ], | ||
| ), | ||
| ), | ||
| ); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Inspect proxy dialog controller creation/disposal.
rg -n -C3 'TextEditingController\(text: currentSettings|hostController\.dispose|portController\.dispose|usernameController\.dispose|passwordController\.dispose' examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dartRepository: RunanywhereAI/runanywhere-sdks
Length of output: 813
Dispose TextEditingControllers after the dialog closes.
The four TextEditingController instances (hostController, portController, usernameController, passwordController) created in this method are never disposed. Repeated dialog opens leak these controller resources. Wrap the showDialog call in a try-finally block to ensure disposal.
♻️ Proposed fix
- await showDialog<void>(
- context: context,
- builder: (dialogContext) => StatefulBuilder(
+ try {
+ await showDialog<void>(
+ context: context,
+ builder: (dialogContext) => StatefulBuilder(
builder: (dialogContext, setDialogState) => AlertDialog(
title: Text(scope.displayName),
content: SingleChildScrollView(
@@
- ),
- );
+ ),
+ );
+ } finally {
+ hostController.dispose();
+ portController.dispose();
+ usernameController.dispose();
+ passwordController.dispose();
+ }📝 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.
| Future<void> _showProxyConfigDialog(ProxyScope scope) async { | |
| final currentSettings = await ProxySettingsService.shared.load(scope); | |
| final hostController = TextEditingController(text: currentSettings.host); | |
| final portController = | |
| TextEditingController(text: currentSettings.port?.toString() ?? ''); | |
| final usernameController = | |
| TextEditingController(text: currentSettings.username); | |
| final passwordController = | |
| TextEditingController(text: currentSettings.password); | |
| var proxyEnabled = currentSettings.enabled && | |
| currentSettings.scheme.isSupportedInExampleApp; | |
| var selectedScheme = currentSettings.scheme.isSupportedInExampleApp | |
| ? currentSettings.scheme | |
| : ProxyScheme.http; | |
| var bypassLocal = currentSettings.bypassLocal; | |
| var showPassword = false; | |
| if (!mounted) return; | |
| await showDialog<void>( | |
| context: context, | |
| builder: (dialogContext) => StatefulBuilder( | |
| builder: (dialogContext, setDialogState) => AlertDialog( | |
| title: Text(scope.displayName), | |
| content: SingleChildScrollView( | |
| child: Column( | |
| mainAxisSize: MainAxisSize.min, | |
| crossAxisAlignment: CrossAxisAlignment.start, | |
| children: [ | |
| SwitchListTile( | |
| contentPadding: EdgeInsets.zero, | |
| title: const Text('Enable Proxy'), | |
| value: proxyEnabled, | |
| onChanged: (value) { | |
| setDialogState(() { | |
| proxyEnabled = value; | |
| }); | |
| }, | |
| ), | |
| const SizedBox(height: AppSpacing.smallMedium), | |
| DropdownButtonFormField<ProxyScheme>( | |
| value: selectedScheme, | |
| decoration: const InputDecoration( | |
| labelText: 'Protocol', | |
| border: OutlineInputBorder(), | |
| ), | |
| items: ProxyScheme.supportedValues | |
| .map( | |
| (scheme) => DropdownMenuItem( | |
| value: scheme, | |
| child: Text(scheme.displayName), | |
| ), | |
| ) | |
| .toList(), | |
| onChanged: (value) { | |
| if (value != null) { | |
| setDialogState(() { | |
| selectedScheme = value; | |
| }); | |
| } | |
| }, | |
| ), | |
| const SizedBox(height: AppSpacing.mediumLarge), | |
| TextField( | |
| controller: hostController, | |
| decoration: const InputDecoration( | |
| labelText: 'Host', | |
| hintText: '127.0.0.1', | |
| border: OutlineInputBorder(), | |
| ), | |
| ), | |
| const SizedBox(height: AppSpacing.mediumLarge), | |
| TextField( | |
| controller: portController, | |
| keyboardType: TextInputType.number, | |
| decoration: const InputDecoration( | |
| labelText: 'Port', | |
| hintText: '7890', | |
| border: OutlineInputBorder(), | |
| ), | |
| ), | |
| const SizedBox(height: AppSpacing.mediumLarge), | |
| TextField( | |
| controller: usernameController, | |
| decoration: const InputDecoration( | |
| labelText: 'Username', | |
| border: OutlineInputBorder(), | |
| ), | |
| ), | |
| const SizedBox(height: AppSpacing.mediumLarge), | |
| TextField( | |
| controller: passwordController, | |
| obscureText: !showPassword, | |
| decoration: InputDecoration( | |
| labelText: 'Password', | |
| border: const OutlineInputBorder(), | |
| suffixIcon: IconButton( | |
| icon: Icon( | |
| showPassword ? Icons.visibility_off : Icons.visibility, | |
| ), | |
| onPressed: () { | |
| setDialogState(() { | |
| showPassword = !showPassword; | |
| }); | |
| }, | |
| ), | |
| ), | |
| ), | |
| const SizedBox(height: AppSpacing.mediumLarge), | |
| SwitchListTile( | |
| contentPadding: EdgeInsets.zero, | |
| title: const Text('Bypass localhost'), | |
| subtitle: const Text( | |
| 'Skip proxy for localhost, 127.0.0.1, and ::1'), | |
| value: bypassLocal, | |
| onChanged: (value) { | |
| setDialogState(() { | |
| bypassLocal = value; | |
| }); | |
| }, | |
| ), | |
| ], | |
| ), | |
| ), | |
| actions: [ | |
| TextButton( | |
| onPressed: () => Navigator.pop(dialogContext), | |
| child: const Text('Cancel'), | |
| ), | |
| TextButton( | |
| onPressed: () async { | |
| final settings = ProxySettings( | |
| enabled: proxyEnabled, | |
| scheme: selectedScheme, | |
| host: hostController.text.trim(), | |
| port: int.tryParse(portController.text.trim()), | |
| username: usernameController.text.trim(), | |
| password: passwordController.text, | |
| bypassLocal: bypassLocal, | |
| ); | |
| final result = | |
| await ProxySettingsService.shared.save(scope, settings); | |
| if (!result.isValid) { | |
| if (mounted) { | |
| ScaffoldMessenger.of(context).showSnackBar( | |
| SnackBar( | |
| content: Text( | |
| result.message ?? 'Invalid proxy settings', | |
| ), | |
| ), | |
| ); | |
| } | |
| return; | |
| } | |
| await _loadProxyConfiguration(scope); | |
| if (mounted) { | |
| ScaffoldMessenger.of(context).showSnackBar( | |
| SnackBar( | |
| content: Text('${scope.displayName} settings saved'), | |
| ), | |
| ); | |
| } | |
| if (dialogContext.mounted) { | |
| Navigator.pop(dialogContext); | |
| } | |
| }, | |
| child: const Text('Save'), | |
| ), | |
| ], | |
| ), | |
| ), | |
| ); | |
| } | |
| Future<void> _showProxyConfigDialog(ProxyScope scope) async { | |
| final currentSettings = await ProxySettingsService.shared.load(scope); | |
| final hostController = TextEditingController(text: currentSettings.host); | |
| final portController = | |
| TextEditingController(text: currentSettings.port?.toString() ?? ''); | |
| final usernameController = | |
| TextEditingController(text: currentSettings.username); | |
| final passwordController = | |
| TextEditingController(text: currentSettings.password); | |
| var proxyEnabled = currentSettings.enabled && | |
| currentSettings.scheme.isSupportedInExampleApp; | |
| var selectedScheme = currentSettings.scheme.isSupportedInExampleApp | |
| ? currentSettings.scheme | |
| : ProxyScheme.http; | |
| var bypassLocal = currentSettings.bypassLocal; | |
| var showPassword = false; | |
| if (!mounted) return; | |
| try { | |
| await showDialog<void>( | |
| context: context, | |
| builder: (dialogContext) => StatefulBuilder( | |
| builder: (dialogContext, setDialogState) => AlertDialog( | |
| title: Text(scope.displayName), | |
| content: SingleChildScrollView( | |
| child: Column( | |
| mainAxisSize: MainAxisSize.min, | |
| crossAxisAlignment: CrossAxisAlignment.start, | |
| children: [ | |
| SwitchListTile( | |
| contentPadding: EdgeInsets.zero, | |
| title: const Text('Enable Proxy'), | |
| value: proxyEnabled, | |
| onChanged: (value) { | |
| setDialogState(() { | |
| proxyEnabled = value; | |
| }); | |
| }, | |
| ), | |
| const SizedBox(height: AppSpacing.smallMedium), | |
| DropdownButtonFormField<ProxyScheme>( | |
| value: selectedScheme, | |
| decoration: const InputDecoration( | |
| labelText: 'Protocol', | |
| border: OutlineInputBorder(), | |
| ), | |
| items: ProxyScheme.supportedValues | |
| .map( | |
| (scheme) => DropdownMenuItem( | |
| value: scheme, | |
| child: Text(scheme.displayName), | |
| ), | |
| ) | |
| .toList(), | |
| onChanged: (value) { | |
| if (value != null) { | |
| setDialogState(() { | |
| selectedScheme = value; | |
| }); | |
| } | |
| }, | |
| ), | |
| const SizedBox(height: AppSpacing.mediumLarge), | |
| TextField( | |
| controller: hostController, | |
| decoration: const InputDecoration( | |
| labelText: 'Host', | |
| hintText: '127.0.0.1', | |
| border: OutlineInputBorder(), | |
| ), | |
| ), | |
| const SizedBox(height: AppSpacing.mediumLarge), | |
| TextField( | |
| controller: portController, | |
| keyboardType: TextInputType.number, | |
| decoration: const InputDecoration( | |
| labelText: 'Port', | |
| hintText: '7890', | |
| border: OutlineInputBorder(), | |
| ), | |
| ), | |
| const SizedBox(height: AppSpacing.mediumLarge), | |
| TextField( | |
| controller: usernameController, | |
| decoration: const InputDecoration( | |
| labelText: 'Username', | |
| border: OutlineInputBorder(), | |
| ), | |
| ), | |
| const SizedBox(height: AppSpacing.mediumLarge), | |
| TextField( | |
| controller: passwordController, | |
| obscureText: !showPassword, | |
| decoration: InputDecoration( | |
| labelText: 'Password', | |
| border: const OutlineInputBorder(), | |
| suffixIcon: IconButton( | |
| icon: Icon( | |
| showPassword ? Icons.visibility_off : Icons.visibility, | |
| ), | |
| onPressed: () { | |
| setDialogState(() { | |
| showPassword = !showPassword; | |
| }); | |
| }, | |
| ), | |
| ), | |
| ), | |
| const SizedBox(height: AppSpacing.mediumLarge), | |
| SwitchListTile( | |
| contentPadding: EdgeInsets.zero, | |
| title: const Text('Bypass localhost'), | |
| subtitle: const Text( | |
| 'Skip proxy for localhost, 127.0.0.1, and ::1'), | |
| value: bypassLocal, | |
| onChanged: (value) { | |
| setDialogState(() { | |
| bypassLocal = value; | |
| }); | |
| }, | |
| ), | |
| ], | |
| ), | |
| ), | |
| actions: [ | |
| TextButton( | |
| onPressed: () => Navigator.pop(dialogContext), | |
| child: const Text('Cancel'), | |
| ), | |
| TextButton( | |
| onPressed: () async { | |
| final settings = ProxySettings( | |
| enabled: proxyEnabled, | |
| scheme: selectedScheme, | |
| host: hostController.text.trim(), | |
| port: int.tryParse(portController.text.trim()), | |
| username: usernameController.text.trim(), | |
| password: passwordController.text, | |
| bypassLocal: bypassLocal, | |
| ); | |
| final result = | |
| await ProxySettingsService.shared.save(scope, settings); | |
| if (!result.isValid) { | |
| if (mounted) { | |
| ScaffoldMessenger.of(context).showSnackBar( | |
| SnackBar( | |
| content: Text( | |
| result.message ?? 'Invalid proxy settings', | |
| ), | |
| ), | |
| ); | |
| } | |
| return; | |
| } | |
| await _loadProxyConfiguration(scope); | |
| if (mounted) { | |
| ScaffoldMessenger.of(context).showSnackBar( | |
| SnackBar( | |
| content: Text('${scope.displayName} settings saved'), | |
| ), | |
| ); | |
| } | |
| if (dialogContext.mounted) { | |
| Navigator.pop(dialogContext); | |
| } | |
| }, | |
| child: const Text('Save'), | |
| ), | |
| ], | |
| ), | |
| ), | |
| ); | |
| } finally { | |
| hostController.dispose(); | |
| portController.dispose(); | |
| usernameController.dispose(); | |
| passwordController.dispose(); | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart`
around lines 240 - 416, The TextEditingController instances created in
_showProxyConfigDialog (hostController, portController, usernameController,
passwordController) are not disposed and leak; wrap the await showDialog(...)
call in a try/finally inside _showProxyConfigDialog so that in the finally block
you call dispose() on each controller (hostController.dispose(),
portController.dispose(), usernameController.dispose(),
passwordController.dispose()) to ensure they are always cleaned up after the
dialog closes or an error occurs.
| OutlinedButton( | ||
| onPressed: () { | ||
| unawaited(_testProxyConfiguration(scope)); | ||
| }, | ||
| style: OutlinedButton.styleFrom( | ||
| foregroundColor: AppColors.primaryGreen, | ||
| ), | ||
| child: const Text('Test'), | ||
| ), |
There was a problem hiding this comment.
Don’t report a proxy test success when the proxy is disabled.
The Test button is always enabled; if the scope is disabled or incomplete, ExampleHttpService.createScopedHttpClient uses DIRECT, so a successful request can be shown as “proxy test succeeded” even though no proxy was used.
🐛 Proposed fix
OutlinedButton(
- onPressed: () {
- unawaited(_testProxyConfiguration(scope));
- },
+ onPressed: isConfigured
+ ? () {
+ unawaited(_testProxyConfiguration(scope));
+ }
+ : null,
style: OutlinedButton.styleFrom(
foregroundColor: AppColors.primaryGreen,
),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart`
around lines 1125 - 1133, The Test button currently always calls
_testProxyConfiguration(scope) which can show "proxy test succeeded" even when
the proxy scope is disabled or incomplete because
ExampleHttpService.createScopedHttpClient falls back to DIRECT; update the UI
and handler so the button is disabled (onPressed == null) when the provided
scope is disabled or missing required fields (e.g., check scope.enabled or
scope.isEnabled and required host/port/auth fields), and/or make
_testProxyConfiguration(scope) return early with an appropriate message if
scope.enabled is false or validation fails; locate the button widget and the
_testProxyConfiguration function and add the guard/validation to prevent running
the test when the proxy is not actually configured.
| flutter_secure_storage: ^9.0.0 | ||
| flutter_secure_storage_windows: | ||
| path: vendor/flutter_secure_storage_windows |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect pinned versions and platform-interface constraints
fd -t f 'pubspec.yaml' examples/flutter/RunAnywhereAI | head -20
cat examples/flutter/RunAnywhereAI/vendor/flutter_secure_storage_windows/pubspec.yaml
fd -t f 'pubspec.lock' examples/flutter/RunAnywhereAI 2>/dev/null | xargs -I {} sh -c 'echo "=== {} ==="; grep -A2 -E "flutter_secure_storage" {}'Repository: RunanywhereAI/runanywhere-sdks
Length of output: 955
🌐 Web query:
flutter_secure_storage 9.0.0 required flutter_secure_storage_windows version
💡 Result:
flutter_secure_storage 9.0.0 requires flutter_secure_storage_windows ^3.0.0 or compatible version that supports FFI with win32 package. The main package depends on the platform-specific flutter_secure_storage_windows package, which was updated in 9.0.0 to migrate Windows implementation to FFI using win32 (from changelog). Do not add flutter_secure_storage_windows directly; use the main flutter_secure_storage: ^9.0.0 in pubspec.yaml, and run flutter pub get to resolve transitive dependencies automatically. The package supports Windows as confirmed by dependencies listed on pub.dev (flutter, flutter_secure_storage_windows, etc.). Latest current version is 10.0.0, but for exactly 9.0.0, the Windows impl version is the one bundled/released with it, likely around 3.x based on similar forks like flutter_secure_storage_x_windows ^3.4.1.
Citations:
- 1: https://pub.dev/packages/flutter_secure_storage/changelog
- 2: https://pub.dev/packages/flutter_secure_storage/versions/9.2.4/changelog
- 3: https://pub.dev/packages/flutter_secure_storage
- 4: https://pub.dev/packages/flutter_secure_storage/install
- 5: https://pub.dev/packages/flutter_secure_storage/versions/9.2.4/license
- 6: https://pub.dev/packages/flutter_secure_storage_windows
🏁 Script executed:
#!/bin/bash
# Check the main pubspec.yaml for dependency_overrides and full dependency tree
head -50 examples/flutter/RunAnywhereAI/pubspec.yaml
tail -20 examples/flutter/RunAnywhereAI/pubspec.yamlRepository: RunanywhereAI/runanywhere-sdks
Length of output: 2069
🏁 Script executed:
#!/bin/bash
# Check if pubspec.lock exists and inspect platform_interface resolution
if [ -f "examples/flutter/RunAnywhereAI/pubspec.lock" ]; then
echo "=== Checking pubspec.lock ==="
grep -A5 "flutter_secure_storage_platform_interface" examples/flutter/RunAnywhereAI/pubspec.lock | head -30
echo ""
echo "=== All flutter_secure_storage entries ==="
grep -B2 -A3 "flutter_secure_storage" examples/flutter/RunAnywhereAI/pubspec.lock | head -60
else
echo "No pubspec.lock found"
fiRepository: RunanywhereAI/runanywhere-sdks
Length of output: 95
🌐 Web query:
flutter_secure_storage_platform_interface 1.1.1 flutter_secure_storage 9.0.0 compatibility
💡 Result:
No, flutter_secure_storage 9.0.0 is not compatible with flutter_secure_storage_platform_interface 1.1.1. The platform interface version 1.1.1 was released about 22 months ago (around mid-2024), while flutter_secure_storage 9.0.0 includes breaking changes like Windows FFI migration (from changelog in 9.2.4 referencing 9.0.0) and depends on a newer version of the platform interface, likely ^2.0.0 or higher as seen in current develop branch (^2.0.1). Newer platform interface versions (2.0+) require Dart SDK 3.3+, while 1.1.1 supports Dart 2.12+, indicating API changes that would cause incompatibility. Pub dependency resolution would fail if constraints do not overlap.
Citations:
- 1: https://pub.dev/packages/flutter_secure_storage_platform_interface/versions
- 2: https://pub.dev/packages/flutter_secure_storage/versions/9.2.4/changelog
- 3: https://github.com/juliansteenbakker/flutter_secure_storage/blob/develop/flutter_secure_storage/pubspec.yaml
- 4: https://pub.dev/packages/flutter_secure_storage/versions
Incompatible version constraints between flutter_secure_storage: ^9.0.0 and the vendored flutter_secure_storage_windows 3.1.2.
The vendored flutter_secure_storage_windows (version 3.1.2) depends on flutter_secure_storage_platform_interface: ^1.1.1, but flutter_secure_storage: ^9.0.0 requires flutter_secure_storage_platform_interface: ^2.0.0 or higher. This constraint mismatch will cause flutter pub get to fail with a dependency resolution error on Windows.
To resolve:
- Upgrade the vendored
flutter_secure_storage_windowsto version 4.x or higher (which supports platform_interface ^2.0.0+), or - Downgrade
flutter_secure_storageto a version compatible with platform_interface ^1.1.1 (e.g.,^8.x), or - Remove the vendored override and rely on the automatic transitive dependency resolution via
flutter_secure_storage: ^9.0.0.
Also applies to: 76-77
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/flutter/RunAnywhereAI/pubspec.yaml` around lines 35 - 37, The
pubspec lists flutter_secure_storage: ^9.0.0 but uses a vendored
flutter_secure_storage_windows (v3.1.2) that requires
flutter_secure_storage_platform_interface ^1.1.1, causing a platform_interface
version conflict; fix by either (preferred) updating the vendored
flutter_secure_storage_windows to a 4.x+ release that supports
flutter_secure_storage_platform_interface ^2.0.0 (replace the vendor folder or
point the dependency to a compatible 4.x source), or remove the vendored
flutter_secure_storage_windows override so pub can resolve the transitive
4.x-compatible Windows package, or alternatively downgrade
flutter_secure_storage to a 8.x release that matches platform_interface
^1.1.1—apply the chosen change in pubspec.yaml and run flutter pub get to verify
resolution.
| UpdateTheme(window); | ||
|
|
||
| return OnCreate(); |
There was a problem hiding this comment.
Tear down the HWND when OnCreate() fails.
Line 149 returns false without destroying the native window created at Line 137, leaving callers with a failed Create() but a live HWND until some later cleanup path runs.
🐛 Proposed fix
UpdateTheme(window);
- return OnCreate();
+ if (!OnCreate()) {
+ Destroy();
+ return false;
+ }
+
+ return true;
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp` around lines
147 - 149, OnCreate() can fail but the code currently returns false without
destroying the HWND created earlier; update the Create() flow so that after
calling UpdateTheme(window) you call OnCreate() and if it returns false you call
::DestroyWindow(window) (and clear the stored HWND/member window handle) before
returning false, ensuring the native window is torn down when OnCreate() fails.
| switch (message) { | ||
| case WM_DESTROY: | ||
| window_handle_ = nullptr; | ||
| OnDestroy(); | ||
| if (quit_on_close_) { | ||
| PostQuitMessage(0); | ||
| } | ||
| return 0; | ||
|
|
||
| case WM_DPICHANGED: { | ||
| auto newRectSize = reinterpret_cast<RECT*>(lparam); | ||
| LONG newWidth = newRectSize->right - newRectSize->left; | ||
| LONG newHeight = newRectSize->bottom - newRectSize->top; | ||
|
|
||
| SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, | ||
| newHeight, SWP_NOZORDER | SWP_NOACTIVATE); | ||
|
|
||
| return 0; | ||
| } | ||
| case WM_SIZE: { | ||
| RECT rect = GetClientArea(); | ||
| if (child_content_ != nullptr) { | ||
| // Size and position the child window. | ||
| MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, | ||
| rect.bottom - rect.top, TRUE); | ||
| } | ||
| return 0; | ||
| } | ||
|
|
||
| case WM_ACTIVATE: | ||
| if (child_content_ != nullptr) { | ||
| SetFocus(child_content_); | ||
| } | ||
| return 0; | ||
|
|
||
| case WM_DWMCOLORIZATIONCOLORCHANGED: | ||
| UpdateTheme(hwnd); | ||
| return 0; | ||
| } | ||
|
|
||
| return DefWindowProc(window_handle_, message, wparam, lparam); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Confirm whether default message handling still uses the member
# HWND after WM_DESTROY clears it.
set -euo pipefail
file="$(fd -i '^win32_window\.cpp$' | head -n 1)"
test -n "$file"
rg -n -C5 'case WM_DESTROY|case WM_NCDESTROY|window_handle_ = nullptr|DefWindowProc\((window_handle_|hwnd)' "$file"
# Expected after the fix:
# - No fallback call to DefWindowProc(window_handle_, ...).
# - A WM_NCDESTROY path clears GWLP_USERDATA and calls DefWindowProc(hwnd, ...).Repository: RunanywhereAI/runanywhere-sdks
Length of output: 969
Pass the HWND parameter instead of the cleared member variable to DefWindowProc, and add WM_NCDESTROY handling.
After WM_DESTROY clears window_handle_, subsequent messages like WM_NCDESTROY fall through to the default handler at line 221. Passing nullptr to DefWindowProc violates the Windows API contract and skips required cleanup. Use the hwnd parameter and add a WM_NCDESTROY handler to clear GWLP_USERDATA:
Proposed fix
case WM_DWMCOLORIZATIONCOLORCHANGED:
UpdateTheme(hwnd);
return 0;
+
+ case WM_NCDESTROY:
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, 0);
+ return DefWindowProc(hwnd, message, wparam, lparam);
}
- return DefWindowProc(window_handle_, message, wparam, lparam);
+ return DefWindowProc(hwnd, message, wparam, lparam);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp` around lines
181 - 221, The switch falls through to DefWindowProc using the cleared member
window_handle_; change the final call to use the incoming hwnd parameter (call
DefWindowProc(hwnd, message, wparam, lparam)) and add a WM_NCDESTROY case that
clears the per-window user data (SetWindowLongPtr(hwnd, GWLP_USERDATA, 0)) and
performs any final cleanup after WM_DESTROY (e.g., ensure window_handle_ is
already nulled and return 0), so default processing gets the correct hwnd and
GWLP_USERDATA is cleared when the non-client destroy message arrives.
| void Win32Window::Destroy() { | ||
| if (window_handle_) { | ||
| DestroyWindow(window_handle_); | ||
| window_handle_ = nullptr; | ||
| } | ||
| if (g_active_window_count == 0) { | ||
| WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Confirm DestroyWindow result handling in Win32Window::Destroy.
set -euo pipefail
file="$(fd -i '^win32_window\.cpp$' | head -n 1)"
test -n "$file"
rg -n -C6 'void Win32Window::Destroy|DestroyWindow\(|window_handle_ = nullptr|UnregisterWindowClass' "$file"
# Expected after the fix:
# - DestroyWindow(...) result is checked.
# - window_handle_ is not cleared on the failure path.
# - class unregistration is skipped when destruction failed.Repository: RunanywhereAI/runanywhere-sdks
Length of output: 1835
Ignore DestroyWindow failure result, causing improper resource cleanup.
DestroyWindow returns a BOOL indicating success, but line 228 ignores this result. Line 229 unconditionally clears window_handle_, and line 232 may unregister the window class even if the window destruction failed. Preserve state and skip class unregistration if DestroyWindow fails.
Proposed fix
void Win32Window::Destroy() {
OnDestroy();
if (window_handle_) {
- DestroyWindow(window_handle_);
- window_handle_ = nullptr;
+ HWND window = window_handle_;
+ if (!DestroyWindow(window)) {
+ return;
+ }
+ if (window_handle_ == window) {
+ window_handle_ = nullptr;
+ }
}
if (g_active_window_count == 0) {
WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp` around lines
224 - 231, The Destroy method currently ignores DestroyWindow's BOOL result and
unconditionally clears window_handle_ and potentially unregisters the class;
update Win32Window::Destroy to capture the return value of
DestroyWindow(window_handle_), only set window_handle_ to nullptr if
DestroyWindow succeeds, and only decrement/consider g_active_window_count and
call WindowClassRegistrar::GetInstance()->UnregisterWindowClass() when
destruction succeeded; if DestroyWindow fails, preserve window_handle_ (so
callers can retry or cleanup later) and surface/log the failure (e.g., via
GetLastError) rather than proceeding to unregister the class.
| if "%~1"=="" goto args_done | ||
| if /I "%~1"=="all" set "BACKENDS=all" | ||
| if /I "%~1"=="llamacpp" set "BACKENDS=llamacpp" | ||
| if /I "%~1"=="onnx" set "BACKENDS=onnx" | ||
| if /I "%~1"=="--clean" set "CLEAN=1" | ||
| if /I "%~1"=="-h" goto usage | ||
| if /I "%~1"=="--help" goto usage | ||
| shift | ||
| goto :parse_args | ||
|
|
||
| :done_args | ||
| goto parse_args |
There was a problem hiding this comment.
Reject unknown build arguments instead of ignoring them.
A typo or stale option like --shared is currently ignored and the script continues with the default backend, which can produce the wrong artifact set while still succeeding.
Proposed argument validation
:parse_args
if "%~1"=="" goto args_done
-if /I "%~1"=="all" set "BACKENDS=all"
-if /I "%~1"=="llamacpp" set "BACKENDS=llamacpp"
-if /I "%~1"=="onnx" set "BACKENDS=onnx"
-if /I "%~1"=="--clean" set "CLEAN=1"
+if /I "%~1"=="all" (
+ set "BACKENDS=all"
+ goto arg_ok
+)
+if /I "%~1"=="llamacpp" (
+ set "BACKENDS=llamacpp"
+ goto arg_ok
+)
+if /I "%~1"=="onnx" (
+ set "BACKENDS=onnx"
+ goto arg_ok
+)
+if /I "%~1"=="--clean" (
+ set "CLEAN=1"
+ goto arg_ok
+)
if /I "%~1"=="-h" goto usage
if /I "%~1"=="--help" goto usage
+echo ERROR: Unknown argument: %~1
+echo Usage: build-windows.bat [all^|llamacpp^|onnx] [--clean]
+exit /b 1
+:arg_ok
shift
goto parse_args🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sdk/runanywhere-commons/scripts/build-windows.bat` around lines 8 - 16, The
argument parsing currently silently ignores unknown options, so update the parse
logic around the if "%~1" checks (the parse_args loop / parse_args label) to
reject unrecognized arguments: when none of the known branches (all, llamacpp,
onnx, --clean, -h, --help) match, print a clear error mentioning the bad token
and call the existing usage flow (goto usage) or exit /b 1; ensure variables
like BACKENDS and CLEAN are only set when recognized and that the parser halts
with non-zero status for unknown options to prevent accidental successful builds
with wrong defaults.
| if /I "%BUILD_ONNX%"=="ON" ( | ||
| for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\bin\*.dll") do ( | ||
| if exist "%%~F" copy /y "%%~F" "%DIST_DIR%\" >nul | ||
| ) | ||
| for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\lib\*.dll") do ( | ||
| if exist "%%~F" copy /y "%%~F" "%DIST_DIR%\" >nul | ||
| ) |
There was a problem hiding this comment.
Fail if Sherpa dependency DLL copies fail.
The ONNX dependency copy commands currently discard failures, so a locked file, permission issue, or full disk can still end with “Build complete” and an incomplete dist.
Proposed copy error handling
if /I "%BUILD_ONNX%"=="ON" (
for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\bin\*.dll") do (
- if exist "%%~F" copy /y "%%~F" "%DIST_DIR%\" >nul
+ if exist "%%~F" (
+ copy /y "%%~F" "%DIST_DIR%\" >nul
+ if errorlevel 1 (
+ echo ERROR: Failed to copy %%~nxF to %DIST_DIR%.
+ exit /b 1
+ )
+ )
)
for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\lib\*.dll") do (
- if exist "%%~F" copy /y "%%~F" "%DIST_DIR%\" >nul
+ if exist "%%~F" (
+ copy /y "%%~F" "%DIST_DIR%\" >nul
+ if errorlevel 1 (
+ echo ERROR: Failed to copy %%~nxF to %DIST_DIR%.
+ exit /b 1
+ )
+ )
)
)📝 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.
| if /I "%BUILD_ONNX%"=="ON" ( | |
| for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\bin\*.dll") do ( | |
| if exist "%%~F" copy /y "%%~F" "%DIST_DIR%\" >nul | |
| ) | |
| for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\lib\*.dll") do ( | |
| if exist "%%~F" copy /y "%%~F" "%DIST_DIR%\" >nul | |
| ) | |
| if /I "%BUILD_ONNX%"=="ON" ( | |
| for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\bin\*.dll") do ( | |
| if exist "%%~F" ( | |
| copy /y "%%~F" "%DIST_DIR%\" >nul | |
| if errorlevel 1 ( | |
| echo ERROR: Failed to copy %%~nxF to %DIST_DIR%. | |
| exit /b 1 | |
| ) | |
| ) | |
| ) | |
| for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\lib\*.dll") do ( | |
| if exist "%%~F" ( | |
| copy /y "%%~F" "%DIST_DIR%\" >nul | |
| if errorlevel 1 ( | |
| echo ERROR: Failed to copy %%~nxF to %DIST_DIR%. | |
| exit /b 1 | |
| ) | |
| ) | |
| ) | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sdk/runanywhere-commons/scripts/build-windows.bat` around lines 100 - 106,
The current Windows batch loop that copies Sherpa ONNX DLLs (the block gated by
BUILD_ONNX) swallows copy failures by redirecting output to nul; change it so
each copy checks the copy exit status (ERRORLEVEL) after the copy of each file
from "%ROOT%\third_party\sherpa-onnx-windows\bin\*.dll" and
"%ROOT%\third_party\sherpa-onnx-windows\lib\*.dll" and, on failure, emit an
error to the console and exit the script with non‑zero status (e.g., exit /b 1)
so the build fails instead of reporting “Build complete” with missing DLLs.
Ensure you apply this check inside both FOR loops that perform the copy to
"%DIST_DIR%\".
| final totalFiles = multiFile.files.length; | ||
| final totalModelBytes = model.downloadSize ?? 0; | ||
| var cumulativeDownloaded = 0; | ||
| _logger.info('Multi-file model: downloading $totalFiles files'); | ||
| yield ModelDownloadProgress.started(modelId, model.downloadSize ?? 0); | ||
| yield ModelDownloadProgress.started(modelId, totalModelBytes); | ||
|
|
||
| for (var i = 0; i < multiFile.files.length; i++) { | ||
| final descriptor = multiFile.files[i]; | ||
| final fileUrl = descriptor.url; | ||
| if (fileUrl == null) { | ||
| _logger.warning('No URL for file descriptor: ${descriptor.destinationPath}'); | ||
| _logger.warning( | ||
| 'No URL for file descriptor: ${descriptor.destinationPath}'); | ||
| continue; |
There was a problem hiding this comment.
Fail invalid multi-file manifests instead of completing partial downloads.
An empty files list or a descriptor with url == null currently falls through to _updateModelLocalPath(...) and publishes downloadCompleted, registering an incomplete model as usable.
Reject incomplete multi-file manifests
final totalFiles = multiFile.files.length;
+ if (totalFiles == 0) {
+ throw StateError('Multi-file model has no files: $modelId');
+ }
final totalModelBytes = model.downloadSize ?? 0;
var cumulativeDownloaded = 0;
_logger.info('Multi-file model: downloading $totalFiles files');
yield ModelDownloadProgress.started(modelId, totalModelBytes);
@@
final fileUrl = descriptor.url;
if (fileUrl == null) {
_logger.warning(
'No URL for file descriptor: ${descriptor.destinationPath}');
- continue;
+ throw StateError(
+ 'No URL for file descriptor: ${descriptor.destinationPath}',
+ );
}📝 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.
| final totalFiles = multiFile.files.length; | |
| final totalModelBytes = model.downloadSize ?? 0; | |
| var cumulativeDownloaded = 0; | |
| _logger.info('Multi-file model: downloading $totalFiles files'); | |
| yield ModelDownloadProgress.started(modelId, model.downloadSize ?? 0); | |
| yield ModelDownloadProgress.started(modelId, totalModelBytes); | |
| for (var i = 0; i < multiFile.files.length; i++) { | |
| final descriptor = multiFile.files[i]; | |
| final fileUrl = descriptor.url; | |
| if (fileUrl == null) { | |
| _logger.warning('No URL for file descriptor: ${descriptor.destinationPath}'); | |
| _logger.warning( | |
| 'No URL for file descriptor: ${descriptor.destinationPath}'); | |
| continue; | |
| final totalFiles = multiFile.files.length; | |
| if (totalFiles == 0) { | |
| throw StateError('Multi-file model has no files: $modelId'); | |
| } | |
| final totalModelBytes = model.downloadSize ?? 0; | |
| var cumulativeDownloaded = 0; | |
| _logger.info('Multi-file model: downloading $totalFiles files'); | |
| yield ModelDownloadProgress.started(modelId, totalModelBytes); | |
| for (var i = 0; i < multiFile.files.length; i++) { | |
| final descriptor = multiFile.files[i]; | |
| final fileUrl = descriptor.url; | |
| if (fileUrl == null) { | |
| _logger.warning( | |
| 'No URL for file descriptor: ${descriptor.destinationPath}'); | |
| throw StateError( | |
| 'No URL for file descriptor: ${descriptor.destinationPath}', | |
| ); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart`
around lines 203 - 215, The multi-file download currently treats empty or
partially missing descriptors as success; before calling _updateModelLocalPath
or emitting downloadCompleted, validate the manifest and abort on invalid data:
if multiFile.files.isEmpty, log an error via _logger.error and emit a failure
progress (e.g. ModelDownloadProgress.failed or throw) and return; likewise
inside the loop if descriptor.url == null, immediately log the error, emit a
failure and stop the download rather than continue to the next file. Ensure
neither _updateModelLocalPath nor the final downloadCompleted progress are
called when the manifest is invalid.
… or download modals
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
Bugbot Autofix is ON, but it could not run because the branch was deleted or merged before autofix could start.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit c29f567. Configure here.
| ProxyScheme.https => | ||
| 'PROXY ${settings.host.trim()}:${settings.port}', | ||
| ProxyScheme.socks5 => null, | ||
| }; |
There was a problem hiding this comment.
HTTPS proxy scheme treated identically to HTTP
Medium Severity
ProxyScheme.https is listed as supported via isSupportedInExampleApp and shown to users, but _proxyDirectiveForSettings generates the exact same PROXY host:port directive for both HTTP and HTTPS schemes. Dart's findProxy PROXY keyword always uses a plain HTTP connection to the proxy. A user selecting HTTPS may expect the proxy connection itself to be TLS-encrypted, but it behaves identically to HTTP, which is misleading and could create a false sense of security.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit c29f567. Configure here.
| Future<bool> testGeneralProxy(Uri uri) async { | ||
| final response = await getJson(uri); | ||
| return response.statusCode >= 200 && response.statusCode < 300; | ||
| } |
There was a problem hiding this comment.
Unused testGeneralProxy method is dead code
Low Severity
testGeneralProxy is a new public method that is never called anywhere in the codebase. The settings UI uses testProxy (which accepts a ProxyScope parameter) instead. This is dead code that adds unnecessary API surface.
Reviewed by Cursor Bugbot for commit c29f567. Configure here.
| } | ||
|
|
||
| _current[scope] = settings; | ||
| return settings; |
There was a problem hiding this comment.
Legacy proxy path reads keychain credentials twice
Low Severity
In load(), credentials are read from the keychain at the top for every call. When the legacy path is taken (scope == ProxyScope.general && !hasScopedPreferences), _loadLegacyGeneralSettings reads the exact same keychain keys again, and the initial reads are silently discarded. This results in redundant async I/O with no functional benefit.
Reviewed by Cursor Bugbot for commit c29f567. Configure here.
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt (2)
284-297:⚠️ Potential issue | 🔴 CriticalSecond duplicate
elseif(RAC_PLATFORM_WINDOWS)branch — dead code.Same problem as the GGML section above: lines 284–288 add a new Windows branch, but the pre-existing Windows branch at lines 294–297 is still present. The chain reaches line 284 first, so lines 294–297 never execute. This is benign here (only a duplicated status message), but it should be removed to avoid confusion and to keep the file consistent with the AI summary.
🛠️ Proposed fix: remove the trailing duplicate
elseif(RAC_PLATFORM_WINDOWS) message(STATUS "Configuring LlamaCPP backend for Windows") target_compile_definitions(rac_backend_llamacpp PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN) target_link_libraries(rac_backend_llamacpp PUBLIC ws2_32) elseif(RAC_PLATFORM_LINUX) message(STATUS "Configuring LlamaCPP backend for Linux") # Linux-specific link libraries target_link_libraries(rac_backend_llamacpp PUBLIC pthread dl) - -elseif(RAC_PLATFORM_WINDOWS) - message(STATUS "Configuring LlamaCPP backend for Windows") - # No extra link libraries needed on Windows (threading is built-in) endif()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt` around lines 284 - 297, Remove the duplicate elseif(RAC_PLATFORM_WINDOWS) branch that follows the existing one: locate the repeated block that sets message(STATUS "Configuring LlamaCPP backend for Windows") and the Windows-specific target_compile_definitions/target_link_libraries for rac_backend_llamacpp, and delete the trailing duplicate so only the first RAC_PLATFORM_WINDOWS branch remains; ensure the remaining branch retains target_compile_definitions(rac_backend_llamacpp PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN) and target_link_libraries(rac_backend_llamacpp PUBLIC ws2_32) and no other changes to the RAC_PLATFORM_LINUX branch.
76-113:⚠️ Potential issue | 🔴 CriticalDuplicate
elseif(RAC_PLATFORM_WINDOWS)branch — second branch is dead code and losesGGML_OPENMP OFF.Two
elseif(RAC_PLATFORM_WINDOWS)branches now exist in the same if/elseif chain (lines 76–85 and 100–113). CMake only evaluates the first matching branch, so the second one is unreachable. In particular,set(GGML_OPENMP OFF ...)from the old block (line 110) is silently dropped on Windows, along with the status message. The AI summary describes removing the duplicate block, but it is still present in the committed code.🛠️ Proposed fix: consolidate into a single Windows branch
elseif(RAC_PLATFORM_WINDOWS) set(GGML_METAL OFF CACHE BOOL "" FORCE) set(GGML_VULKAN OFF CACHE BOOL "" FORCE) set(GGML_CUDA OFF CACHE BOOL "" FORCE) set(GGML_OPENCL OFF CACHE BOOL "" FORCE) set(GGML_HIPBLAS OFF CACHE BOOL "" FORCE) set(GGML_SYCL OFF CACHE BOOL "" FORCE) set(GGML_KOMPUTE OFF CACHE BOOL "" FORCE) set(GGML_RPC OFF CACHE BOOL "" FORCE) set(GGML_NATIVE OFF CACHE BOOL "" FORCE) + set(GGML_OPENMP OFF CACHE BOOL "" FORCE) + message(STATUS "Configuring llama.cpp for Windows (CPU-only)") elseif(RAC_PLATFORM_LINUX) # Disable GPU backends not typically available on Linux ARM set(GGML_METAL OFF CACHE BOOL "" FORCE) set(GGML_CUDA OFF CACHE BOOL "" FORCE) set(GGML_VULKAN OFF CACHE BOOL "" FORCE) set(GGML_OPENCL OFF CACHE BOOL "" FORCE) set(GGML_HIPBLAS OFF CACHE BOOL "" FORCE) set(GGML_SYCL OFF CACHE BOOL "" FORCE) # Enable ARM NEON for aarch64 (Raspberry Pi 5) if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") set(GGML_NEON ON CACHE BOOL "" FORCE) message(STATUS "Enabling NEON for Linux aarch64") endif() -elseif(RAC_PLATFORM_WINDOWS) - # Windows: CPU-only by default, no Metal/CUDA/Vulkan - set(GGML_METAL OFF CACHE BOOL "" FORCE) - set(GGML_CUDA OFF CACHE BOOL "" FORCE) - set(GGML_VULKAN OFF CACHE BOOL "" FORCE) - set(GGML_OPENCL OFF CACHE BOOL "" FORCE) - set(GGML_HIPBLAS OFF CACHE BOOL "" FORCE) - set(GGML_SYCL OFF CACHE BOOL "" FORCE) - set(GGML_KOMPUTE OFF CACHE BOOL "" FORCE) - set(GGML_RPC OFF CACHE BOOL "" FORCE) - set(GGML_OPENMP OFF CACHE BOOL "" FORCE) - set(GGML_NATIVE OFF CACHE BOOL "" FORCE) - message(STATUS "Configuring llama.cpp for Windows (CPU-only)") endif()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt` around lines 76 - 113, There are two elseif(RAC_PLATFORM_WINDOWS) branches; remove the duplicate earlier branch and consolidate into a single Windows branch that sets GGML_METAL, GGML_VULKAN, GGML_CUDA, GGML_OPENCL, GGML_HIPBLAS, GGML_SYCL, GGML_KOMPUTE, GGML_RPC, GGML_NATIVE OFF (CACHE BOOL "" FORCE) and also set GGML_OPENMP OFF (CACHE BOOL "" FORCE) and emit the message(STATUS "Configuring llama.cpp for Windows (CPU-only)"); update or delete the first occurrence so the final if/elseif chain contains only one RAC_PLATFORM_WINDOWS handling and preserves GGML_OPENMP OFF and the status message.sdk/runanywhere-commons/scripts/windows/download-sherpa-onnx.bat (1)
66-71:⚠️ Potential issue | 🟡 MinorAdd
--fail(or-f) to curl to reject HTTP error responses.Without
--fail, curl treats a 4xx/5xx response as success and writes the error body tosherpa-onnx.tar.bz2. The errorlevel check on Line 67 then won't trigger; the failure only surfaces later as a misleading "Extraction failed."--failalso interacts correctly with--retry, so transient 5xx responses will actually be retried.Proposed fix
-curl -L --retry 5 --retry-delay 2 --retry-all-errors -o "%TEMP_DL%\sherpa-onnx.tar.bz2" "%URL%" +curl -fL --retry 5 --retry-delay 2 --retry-all-errors -o "%TEMP_DL%\sherpa-onnx.tar.bz2" "%URL%"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/scripts/windows/download-sherpa-onnx.bat` around lines 66 - 71, The curl invocation that downloads "%TEMP_DL%\sherpa-onnx.tar.bz2" should include the --fail (or -f) flag so HTTP 4xx/5xx responses cause curl to exit non‑zero and trigger the existing errorlevel branch; update the curl command (the line starting with curl -L --retry 5 ...) to add --fail (or -f) alongside the existing flags so transient 5xx responses are still retried and plain error pages are not written to the tarball.
♻️ Duplicate comments (9)
sdk/runanywhere-commons/scripts/build-windows.bat (2)
100-107:⚠️ Potential issue | 🟠 MajorSherpa DLL copy failures are still swallowed.
The
if exist ... copy /y ... >nulpattern discards the copy exit code, so a locked file, AV hold, or permission error silently completes with "Build complete" and an incompletedist. Please checkerrorlevelafter each copy and abort, as suggested previously.Proposed fix
if /I "%BUILD_ONNX%"=="ON" ( for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\bin\*.dll") do ( - if exist "%%~F" copy /y "%%~F" "%DIST_DIR%\" >nul + if exist "%%~F" ( + copy /y "%%~F" "%DIST_DIR%\" >nul + if errorlevel 1 ( + echo ERROR: Failed to copy %%~nxF to %DIST_DIR%. + exit /b 1 + ) + ) ) for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\lib\*.dll") do ( - if exist "%%~F" copy /y "%%~F" "%DIST_DIR%\" >nul + if exist "%%~F" ( + copy /y "%%~F" "%DIST_DIR%\" >nul + if errorlevel 1 ( + echo ERROR: Failed to copy %%~nxF to %DIST_DIR%. + exit /b 1 + ) + ) ) )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/scripts/build-windows.bat` around lines 100 - 107, The for-loop copy blocks guarded by the BUILD_ONNX check (the two for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\bin\*.dll") and for %%F in ("%ROOT%\third_party\sherpa-onnx-windows\lib\*.dll") that copy to "%DIST_DIR%\") currently redirect output to >nul and therefore swallow failures; after each copy command check the ERRORLEVEL (or use conditional &&/||) and if the copy failed, print an error via echo and exit /b with a non-zero code so the build aborts and the failure is visible.
7-16:⚠️ Potential issue | 🟡 MinorUnknown arguments are still silently ignored.
A typo like
build-windows.bat onnxxfalls throughparse_argsleavingBACKENDS=llamacppand produces a silently wrong artifact set. Please reject unrecognized tokens with a non-zero exit and a usage message, as previously suggested.Proposed fix
:parse_args if "%~1"=="" goto args_done -if /I "%~1"=="all" set "BACKENDS=all" -if /I "%~1"=="llamacpp" set "BACKENDS=llamacpp" -if /I "%~1"=="onnx" set "BACKENDS=onnx" -if /I "%~1"=="--clean" set "CLEAN=1" -if /I "%~1"=="-h" goto usage -if /I "%~1"=="--help" goto usage +if /I "%~1"=="all" ( set "BACKENDS=all" & goto arg_ok ) +if /I "%~1"=="llamacpp" ( set "BACKENDS=llamacpp" & goto arg_ok ) +if /I "%~1"=="onnx" ( set "BACKENDS=onnx" & goto arg_ok ) +if /I "%~1"=="--clean" ( set "CLEAN=1" & goto arg_ok ) +if /I "%~1"=="-h" goto usage +if /I "%~1"=="--help" goto usage +echo ERROR: Unknown argument: %~1 +goto usage +:arg_ok shift goto parse_args🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/scripts/build-windows.bat` around lines 7 - 16, The parse_args routine (label :parse_args) currently ignores unknown tokens and can leave BACKENDS set incorrectly; update it so that after the recognized checks for "all", "llamacpp", "onnx", "--clean", "-h", and "--help" any unrecognized argument triggers printing the usage (reuse the existing usage handling) and exits non-zero (set ERROR or call exit /b 1) instead of silently continuing; ensure the unknown-arg branch runs before the shift/goto parse_args loop so tokens like "onnxx" cause an immediate usage message and non-zero exit rather than falling through and leaving BACKENDS incorrectly.examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart (2)
1125-1133:⚠️ Potential issue | 🟠 MajorTest button can report "success" when the proxy is disabled/incomplete.
The Test button is always enabled. When
isConfiguredis false,_testProxyConfigurationstill runs, andExampleHttpService.createScopedHttpClientfalls back toDIRECT— so the snackbar reports"${scope.displayName} test succeeded"even though no proxy was actually exercised. Gate theonPressedonisConfigured(or early-return inside_testProxyConfigurationwith an explicit message).🐛 Proposed fix
OutlinedButton( - onPressed: () { - unawaited(_testProxyConfiguration(scope)); - }, + onPressed: isConfigured + ? () { + unawaited(_testProxyConfiguration(scope)); + } + : null, style: OutlinedButton.styleFrom( foregroundColor: AppColors.primaryGreen, ), child: const Text('Test'), ),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart` around lines 1125 - 1133, The Test button is always enabled so _testProxyConfiguration runs even when a proxy is not configured and ExampleHttpService.createScopedHttpClient falls back to DIRECT, causing false "test succeeded" snackbars; fix by gating the OutlinedButton's onPressed on the setting that indicates a configured proxy (isConfigured) or add an early return at the start of _testProxyConfiguration that checks isConfigured and shows an explicit snackbar/error instead of proceeding; update the OutlinedButton(onPressed: ...) to only call unawaited(_testProxyConfiguration(scope)) when isConfigured is true (or have _testProxyConfiguration(scope) check isConfigured and return with a clear message using scope.displayName).
240-416:⚠️ Potential issue | 🟡 MinorDispose
TextEditingControllers after the dialog closes.
hostController,portController,usernameController, andpasswordControllerare allocated on every call to_showProxyConfigDialogand never disposed, leaking controllers each time the user opens the dialog. Wrap theawait showDialog(...)in atry/finallythat disposes all four.♻️ Proposed fix
- await showDialog<void>( - context: context, - builder: (dialogContext) => StatefulBuilder( - ... - ), - ); + try { + await showDialog<void>( + context: context, + builder: (dialogContext) => StatefulBuilder( + ... + ), + ); + } finally { + hostController.dispose(); + portController.dispose(); + usernameController.dispose(); + passwordController.dispose(); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dart` around lines 240 - 416, The four TextEditingController instances (hostController, portController, usernameController, passwordController) created in _showProxyConfigDialog are never disposed; fix by disposing them on all paths: after creating the controllers, if (!mounted) dispose each and return; otherwise wrap the await showDialog(...) call in try { await showDialog(...); } finally { hostController.dispose(); portController.dispose(); usernameController.dispose(); passwordController.dispose(); } so controllers are always cleaned up even if the dialog is dismissed or an early return occurs.sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart (1)
203-283:⚠️ Potential issue | 🟠 MajorInvalid multi-file manifests still complete successfully.
Two paths let an invalid manifest end in
downloadCompleted:
totalFiles == 0(emptymultiFile.files) — the loop doesn't execute, then_updateModelLocalPathandModelDownloadProgress.completedstill fire, registering an empty directory as a usable model.- A descriptor with
url == null(line 212) iscontinued past; if every descriptor has a null URL, or some succeed and others silently skip, the model is still marked completed with missing files.Fail fast in both cases so partial/empty models aren't registered.
🐛 Proposed fix
final totalFiles = multiFile.files.length; + if (totalFiles == 0) { + throw StateError('Multi-file model has no files: $modelId'); + } final totalModelBytes = model.downloadSize ?? 0; var cumulativeDownloaded = 0; @@ final fileUrl = descriptor.url; if (fileUrl == null) { - _logger.warning( - 'No URL for file descriptor: ${descriptor.destinationPath}'); - continue; + throw StateError( + 'No URL for file descriptor: ${descriptor.destinationPath}', + ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dart` around lines 203 - 283, Detect and fail fast for empty or invalid multi-file manifests: before yielding ModelDownloadProgress.started check if multiFile.files.isEmpty and throw/log and yield a failed/aborted progress instead of continuing; inside the for-loop change the handling of descriptor.url (currently continuing on null) to immediately fail (throw an exception or yield a failed ModelDownloadProgress) when descriptor.url == null so the loop aborts and _updateModelLocalPath / ModelDownloadProgress.completed are not reached for empty/partial downloads; refer to multiFile.files, descriptor.url, ModelDownloadProgress.started, ModelDownloadProgress.completed, and _updateModelLocalPath to locate and update the logic.examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp (3)
147-149:⚠️ Potential issue | 🟡 Minor
OnCreate()failure still leaks the created HWND.If
OnCreate()returnsfalse,Create()returnsfalsewhile the HWND from Line 137 remains alive (pointing at a half-initializedWin32Window), andwindow_handle_is still set. Subsequent messages may route into the subclass whose setup failed, until the destructor eventually runs. CallDestroyWindow(window)/Destroy()on the failure path before returningfalse.🐛 Proposed fix
UpdateTheme(window); - return OnCreate(); + if (!OnCreate()) { + Destroy(); + return false; + } + + return true; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp` around lines 147 - 149, If Create() sets window_handle_ and then calls OnCreate(), but OnCreate() returns false you must clean up the partially-created HWND to avoid leaking a half-initialized Win32Window; modify Create() so that when OnCreate() returns false it calls DestroyWindow(window) (or this->Destroy()) and clears/reset window_handle_ before returning false, referencing the existing Create(), OnCreate(), window_handle_, DestroyWindow(window) and Destroy() symbols to locate the fix.
224-231:⚠️ Potential issue | 🟠 Major
DestroyWindowreturn value is still ignored.
DestroyWindowreturns aBOOL; Line 226 discards it, Line 227 clearswindow_handle_unconditionally, and Lines 229‑231 may unregister the class even when the window was not actually destroyed. Check the return, retainwindow_handle_on failure, and only unregister the class on success.🛠 Proposed fix
void Win32Window::Destroy() { if (window_handle_) { - DestroyWindow(window_handle_); - window_handle_ = nullptr; + HWND window = window_handle_; + if (!DestroyWindow(window)) { + return; + } + if (window_handle_ == window) { + window_handle_ = nullptr; + } } if (g_active_window_count == 0) { WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp` around lines 224 - 231, In Win32Window::Destroy, stop ignoring DestroyWindow's BOOL return: call BOOL destroyed = DestroyWindow(window_handle_); only set window_handle_ = nullptr if destroyed is nonzero; if destroyed is zero, retain the handle and optionally log or handle the failure; decrement/unregister the window class and call WindowClassRegistrar::GetInstance()->UnregisterWindowClass() only when the destroy actually succeeded and g_active_window_count indicates no active windows. Ensure you reference window_handle_, DestroyWindow, g_active_window_count, and WindowClassRegistrar::GetInstance()->UnregisterWindowClass() in the fix.
181-221:⚠️ Potential issue | 🟠 Major
DefWindowProcis still invoked with the clearedwindow_handle_, andWM_NCDESTROYis unhandled.After
WM_DESTROYzeroswindow_handle_at Line 183, subsequent messages (notablyWM_NCDESTROY) fall through to Line 221 and passnullptrtoDefWindowProc, which violates the Win32 API contract and skips default non-client cleanup. Use thehwndparameter and add aWM_NCDESTROYcase that clearsGWLP_USERDATA:🛠 Proposed fix
case WM_DWMCOLORIZATIONCOLORCHANGED: UpdateTheme(hwnd); return 0; + + case WM_NCDESTROY: + SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); + return DefWindowProc(hwnd, message, wparam, lparam); } - return DefWindowProc(window_handle_, message, wparam, lparam); + return DefWindowProc(hwnd, message, wparam, lparam);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/windows/runner/win32_window.cpp` around lines 181 - 221, The code currently calls DefWindowProc(window_handle_, message, wparam, lparam) after WM_DESTROY has cleared window_handle_, and it doesn't handle WM_NCDESTROY; change the final fallback to call DefWindowProc(hwnd, message, wparam, lparam) (use the incoming hwnd parameter, not window_handle_), and add a WM_NCDESTROY case that clears the per-window user data (call SetWindowLongPtr(hwnd, GWLP_USERDATA, 0)) before returning DefWindowProc(hwnd, message, wparam, lparam) so default non-client cleanup runs correctly.examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart (1)
27-56:⚠️ Potential issue | 🟠 MajorEnd-to-end timeouts still missing on
getJsonandtestProxy.
connectionTimeout = 15sat Line 72 only bounds TCP connect;request.close()and the body read at Lines 34‑35 / 51 can still hang indefinitely on a stalled proxy or server, blocking tool HTTP calls and the “Test proxy” UX path. Wraprequest.close()andresponse.transform(utf8.decoder).join()with.timeout(...)(sharing a constant between both methods) and handleTimeoutExceptionso theclient.close(force: true)infinallystill runs.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart` around lines 27 - 56, Both getJson and testProxy need end-to-end timeouts: define a shared Duration constant (e.g., requestTimeout) near your connectionTimeout and wrap the async calls that can hang—specifically the request.close() and, in getJson, the response.transform(utf8.decoder).join()—with .timeout(requestTimeout) and catch TimeoutException (from dart:async) to return a sensible failure (e.g., ExampleHttpResponse with an error status or false for testProxy); keep the existing finally blocks so client.close(force: true) always runs.
🧹 Nitpick comments (12)
sdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction_stub.cpp (1)
23-25: Prefer value-initialization over positional aggregate init forout_result.
*out_result = {0, 0, 0, 0};hard-codes an assumption about the exact field count ofrac_extraction_result_t. If a field is ever added, reordered, or changed to a non-scalar type, this will either silently skip zero-initializing the new field or fail to compile — neither of which is caught locally since this is a stub. Elsewhere in the codebase (e.g.,download_orchestrator.cpp:494andrunanywhere_commons_jni.cpp:4433) the idiomrac_extraction_result_t result = {};is used consistently. Match that convention here for resilience:♻️ Proposed change
if (out_result) { - *out_result = {0, 0, 0, 0}; + *out_result = {}; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction_stub.cpp` around lines 23 - 25, The assignment *out_result = {0, 0, 0, 0}; assumes the exact aggregate layout of rac_extraction_result_t and risks missing new fields; replace it with value-initialization so new fields are zero-initialized safely — e.g. use *out_result = {}; or construct a rac_extraction_result_t tmp = {}; *out_result = tmp; to initialize out_result via value-initialization (referencing the out_result variable and rac_extraction_result_t type in the stub).sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart (1)
91-112: Nit:bodyLengthis UTF-16 code units, not bytes.
String.length/response.body.lengthreturn UTF-16 code-unit counts, which diverge from the on-wire byte count once the payload contains non-ASCII (e.g., unicode device names or error messages). For diagnostic logs this is usually fine, but if downstream tooling assumes bytes, considerutf8.encode(requestJson).length(orresponse.bodyBytes.length) and perhaps rename tobodyCharsotherwise.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dart` around lines 91 - 112, The metadata helpers _authRequestLogMetadata and _authResponseLogMetadata currently record bodyLength using String.length (UTF-16 code units); change them to report actual byte lengths by using utf8-encoding for the request (use utf8.encode(requestJson).length) and response.bodyBytes.length for the response, and update the metadata key to a clear name like bodyBytes (or if you prefer to keep the original name, also add a comment clarifying it is a byte count) so downstream tooling gets the on-wire byte count.examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart (1)
16-19: Hard-coded "Windows vertical-slice" wording is unnecessary coupling.
unsupportedMessagealways attributes the gap to the Windows vertical-slice build, but if more platform-specific capability flags are added in the future (e.g., iOS-only features), the message will become inaccurate. Consider deriving the feature label from the triggering condition rather than hard-coding a platform reference, so the message scales with additional gates.Note:
Platform.isWindowsis safe here since Flutter web is not a supported target for this example app.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dart` around lines 16 - 19, The message in unsupportedMessage is hard-coded to "Windows vertical-slice" which couples the text to one platform; update unsupportedMessage and the supportsRag logic to produce a platform-derived message instead (use Platform.operatingSystem or similar) so the text reflects the actual runtime OS that gated the feature; modify unsupportedMessage(String featureName) to return "$featureName is not enabled on ${Platform.operatingSystem}." (or an equivalent generic platform-derived phrase) and ensure any checks referencing Platform.isWindows (e.g., supportsRag) remain accurate but no longer dictate a hard-coded string.examples/flutter/RunAnywhereAI/lib/features/rag/rag_demo_view.dart (1)
222-234: Initialization order:_viewModelcreated before capability check, but has minimal cost.The
RAGViewModelis instantiated in the field initializer (line 37) and the listener is registered ininitState(line 55), both before the capability check inbuild()(line 224). However, the constructor does not touch the SDK—only field initializers allocate primitive state (strings, bools, lists) and extendChangeNotifier. SDK operations (ragCreatePipeline,ragIngest,ragQuery) only occur inside method calls triggered by user actions (loadDocument,askQuestion), which are unreachable ifsupportsRagis false. Thedispose()method correctly cleans up resources. If you want to avoid allocating the ViewModel at all on unsupported platforms, you could lazily instantiate it inbuild()only when needed, though the current pattern is acceptable.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/rag/rag_demo_view.dart` around lines 222 - 234, The RAGViewModel (_viewModel) is currently created in the field initializer even when PlatformCapabilityService.shared.supportsRag is false; to avoid allocating it on unsupported platforms, make _viewModel nullable, remove the field initializer, and lazily instantiate it inside build() only when capability.supportsRag is true (use PlatformCapabilityService.shared to check). When you create the instance, register its listener (the same logic now in initState) and ensure dispose() still removes the listener and disposes the view model if non-null; keep RAGViewModel, _viewModel, initState, build, dispose, and supportsRag as the reference points.sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt (1)
119-131: Windows post-fetch compile tweaks look reasonable.Gating
target_compile_options(llama PRIVATE /Zc:char8_t-)and thecommontarget downgrade to C++17 insideif(TARGET …)guards is correct, sinceFetchContent_MakeAvailablemay not always produce those targets depending on llama.cpp options. One consideration:/Zc:char8_t-is MSVC-specific — if a Windows build ever uses clang-cl or MinGW, this flag will warn/fail. Consider guarding withif(MSVC)for robustness.♻️ Optional: guard MSVC-only flag
if(RAC_PLATFORM_WINDOWS) - if(TARGET llama) + if(MSVC AND TARGET llama) target_compile_options(llama PRIVATE /Zc:char8_t-) endif() if(TARGET common) set_target_properties(common PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF ) endif() endif()🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt` around lines 119 - 131, The post-fetch Windows tweaks add the MSVC-only flag /Zc:char8_t- via target_compile_options(llama PRIVATE /Zc:char8_t-) which can break builds using clang-cl or MinGW; wrap that call in an MSVC-specific guard (e.g., check if(MSVC) before invoking target_compile_options for target "llama") while leaving the target property adjustments for "common" unchanged so the C++17 downgrade still applies on Windows but the compiler-specific flag is only added when MSVC is in use.examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart (1)
32-36:_hasCameraDevicedefaults totruebefore enumeration runs.UI reading
hasCameraDevicebeforeinitializeCamera()completes will briefly seetrueeven on a device without cameras. Initializing tofalse(or a tri-state/null) avoids a momentarily misleading state in the view.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart` around lines 32 - 36, _hasCameraDevice is initialized to true causing a brief incorrect UI state; change its initial value to false (or null if using a tri-state) and ensure initializeCamera() sets _hasCameraDevice based on actual enumeration results and calls notifyListeners()/updates any public getter (e.g., hasCameraDevice) after detection so the view only sees the true value when a camera is confirmed; update references to _hasCameraDevice and any getters to handle the new initial state.examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart (2)
33-44:device.handle! as CameraDescriptionwill throw if misused.
VisionCameraBackend.createSessionis part of the public surface; aVisionCameraDeviceconstructed from a test fake, another backend, or any code path that omitshandlewill hit a null-assertion crash rather than a helpful error. Consider a typed check with a clear exception:♻️ Proposed change
`@override` VisionCameraSession createSession(VisionCameraDevice device) { - final description = device.handle! as CameraDescription; + final handle = device.handle; + if (handle is! CameraDescription) { + throw ArgumentError( + 'CameraPluginVisionCameraBackend requires a device produced by its own ' + 'listDevices(); got handle of type ${handle.runtimeType}.', + ); + } return _CameraPluginVisionCameraSession( CameraController( - description, + handle, ResolutionPreset.medium, imageFormatGroup: ImageFormatGroup.bgra8888, ), ); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart` around lines 33 - 44, CameraPluginVisionCameraBackend.createSession currently force-unwraps device.handle which can cause a null-assertion crash; update createSession to validate that device.handle is non-null and of type CameraDescription before using it (check device.handle != null && device.handle is CameraDescription), and throw a clear exception (e.g., ArgumentError/StateError) with context if the check fails so callers get a helpful error instead of a runtime crash; then cast to CameraDescription and proceed to construct the CameraController and return the existing _CameraPluginVisionCameraSession as before.
4-4:VisionCameraLensDirection.unknownis defined but never produced.The switch at lines 54–58 maps all three current
CameraLensDirectionvalues explicitly, leavingunknownas dead code. If thecamerapackage adds a new enum value, the exhaustive switch will fail at compile-time until this code is updated. Consider either removingunknownor adding a catch-all case for forward compatibility.♻️ Proposed change
lensDirection: switch (camera.lensDirection) { CameraLensDirection.front => VisionCameraLensDirection.front, CameraLensDirection.back => VisionCameraLensDirection.back, CameraLensDirection.external => VisionCameraLensDirection.external, + _ => VisionCameraLensDirection.unknown, },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dart` at line 4, The VisionCameraLensDirection enum contains an "unknown" value that's never produced by the current mapping from the camera package's CameraLensDirection; update the mapping code that converts CameraLensDirection (the switch at lines 54–58) to include a catch-all branch (default/fallback) that returns VisionCameraLensDirection.unknown for unexpected/new CameraLensDirection values, or remove the unknown member if you prefer strict mapping—modify the switch in the conversion function so it explicitly handles front/back/external and uses a default case to map any other value to VisionCameraLensDirection.unknown for forward compatibility.sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt (2)
38-41: Hard-codedx64path won't match ARM64 Windows builds.Both candidate paths bake in
windows/x64/rac_commons.dll. Flutter desktop on Windows supports ARM64, and CMake already exposes the target architecture (e.g.,CMAKE_SYSTEM_PROCESSOR/CMAKE_GENERATOR_PLATFORM). If someone builds the app for ARM64, configuration will hard-fail with a misleading error even if an ARM64rac_commons.dllis available. Consider deriving the arch segment from the build configuration so the same CMakeLists works for both.💡 Sketch of an arch-aware lookup
-set(COMMONS_DLL_CANDIDATES - "${REPO_ROOT_FROM_APP}/sdk/runanywhere-commons/dist/windows/x64/rac_commons.dll" - "${PACKAGE_ROOT}/../../../runanywhere-commons/dist/windows/x64/rac_commons.dll" -) +if(CMAKE_GENERATOR_PLATFORM MATCHES "ARM64|arm64") + set(_RAC_ARCH "arm64") +else() + set(_RAC_ARCH "x64") +endif() +set(COMMONS_DLL_CANDIDATES + "${REPO_ROOT_FROM_APP}/sdk/runanywhere-commons/dist/windows/${_RAC_ARCH}/rac_commons.dll" + "${PACKAGE_ROOT}/../../../runanywhere-commons/dist/windows/${_RAC_ARCH}/rac_commons.dll" +)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt` around lines 38 - 41, The hard-coded "windows/x64" path in COMMONS_DLL_CANDIDATES will fail for ARM64 builds; update CMakeLists to compute an architecture token (e.g., set ARCH to "x64" or "arm64" by inspecting CMAKE_SYSTEM_PROCESSOR and CMAKE_GENERATOR_PLATFORM) and use that token in the candidate paths instead of the literal "x64" so COMMONS_DLL_CANDIDATES points to "${REPO_ROOT_FROM_APP}/sdk/runanywhere-commons/dist/windows/${ARCH}/rac_commons.dll" and the PACKAGE_ROOT alternative; ensure the ARCH detection covers common values like "AMD64"/"x86_64" → x64 and "ARM64"/"aarch64" → arm64 and falls back to x64 if unknown.
35-55: Relative path fromCMAKE_SOURCE_DIRis fragile for out-of-monorepo consumers.
REPO_ROOT_FROM_APPis computed as${CMAKE_SOURCE_DIR}/../../../../. When the plugin is built via the in-repo example app (examples/flutter/RunAnywhereAI/windows), this correctly resolves to the monorepo root. However, if this package is ever consumed by a Flutter app outside the monorepo (e.g., after publishing),CMAKE_SOURCE_DIRwill point to that app'swindows/folder and the computed path will be nonsense — only the secondPACKAGE_ROOT-based candidate can succeed.The
FATAL_ERRORat Line 52 also echoes the raw candidate list and tells users to runscripts/build-windows.bat, a path that only exists inside this monorepo. For now this is acceptable (the plugin is consumed only from within the repo), but please add a README note and/or guard theREPO_ROOT_FROM_APPcandidate behind a sanity check (e.g., verifysdk/runanywhere-commonsexists at that path) before shipping externally.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt` around lines 35 - 55, The REPO_ROOT_FROM_APP candidate is fragile when CMAKE_SOURCE_DIR points at an external app; before adding "${REPO_ROOT_FROM_APP}/sdk/runanywhere-commons/..." to COMMONS_DLL_CANDIDATES, check that the directory "${REPO_ROOT_FROM_APP}/sdk/runanywhere-commons" actually exists and only then append that candidate (leave the PACKAGE_ROOT-based candidate untouched); also update the message(FATAL_ERROR) call to avoid echoing internal repo-only paths or instructing users to run scripts/build-windows.bat (use a generic message like "Missing rac_commons.dll; ensure runanywhere-commons is available or built") and add a short README note explaining that the REPO_ROOT_FROM_APP lookup is monorepo-specific and consumers must provide the DLL or build runanywhere-commons.examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart (1)
123-131: Consider movingconfigureDownloadHttpClientFactoryearlier, before module registration, as defensive programming.Currently,
configureDownloadHttpClientFactoryis installed at line 123 — after_registerModulesAndModels()(line 105) and beforeModelManager.shared.refresh()(line 131). Whilerefresh()only notifies listeners and performs no network I/O, andaddModel/registerModelcurrently persist metadata without triggering downloads, moving the factory configuration to run immediately afterRunAnywhere.initialize()would make the code more robust against future changes to model registration or discovery logic.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart` around lines 123 - 131, Move the RunAnywhere.configureDownloadHttpClientFactory call to immediately after RunAnywhere.initialize() and before _registerModulesAndModels() so the download HTTP client factory is set up before any module/model registration or discovery; update the placement of the configureDownloadHttpClientFactory invocation (currently near ModelManager.shared.refresh()) to directly follow RunAnywhere.initialize() to defensively ensure downloads use the configured factory during registerModel/addModel or any future changes to ModelManager.shared.refresh().examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart (1)
90-104: Prefer pre-registering proxy credentials overauthenticateProxycallback.
authenticateProxyis invoked on each 407 challenge; since the proxy host/port are already known fromsettings, callingclient.addProxyCredentials(settings.host.trim(), settings.port!, '', HttpClientBasicCredentials(...))once afterfindProxyis set is simpler and avoids re-adding credentials on every challenge. The callback form is also brittle if the proxy advertises a realm that differs from''. Functionally not broken, but cleaner.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dart` around lines 90 - 104, Replace the per-407 authenticateProxy callback with a one-time pre-registration of proxy credentials: after you set findProxy, if settings.username is not empty and settings.port is not null, call client.addProxyCredentials(settings.host.trim(), settings.port!, '', HttpClientBasicCredentials(settings.username, settings.password)) once (and remove the client.authenticateProxy = ... block); this avoids re-adding credentials on every 407 and prevents brittle behavior when the proxy realm differs from '' while keeping the same credential values.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1ffcdb3b-912b-48be-9a91-d9c6c247d7e4
⛔ Files ignored due to path filters (1)
examples/flutter/RunAnywhereAI/windows/runner/resources/app_icon.icois excluded by!**/*.ico
📒 Files selected for processing (99)
.gitignoredocs/building.mdexamples/flutter/RunAnywhereAI/.gitignoreexamples/flutter/RunAnywhereAI/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.javaexamples/flutter/RunAnywhereAI/ios/Runner/GeneratedPluginRegistrant.mexamples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dartexamples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dartexamples/flutter/RunAnywhereAI/lib/core/design_system/unsupported_feature_view.dartexamples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dartexamples/flutter/RunAnywhereAI/lib/core/services/example_http_service.dartexamples/flutter/RunAnywhereAI/lib/core/services/platform_capability_service.dartexamples/flutter/RunAnywhereAI/lib/core/services/proxy_settings_service.dartexamples/flutter/RunAnywhereAI/lib/core/utilities/constants.dartexamples/flutter/RunAnywhereAI/lib/features/chat/chat_interface_view.dartexamples/flutter/RunAnywhereAI/lib/features/models/add_model_from_url_view.dartexamples/flutter/RunAnywhereAI/lib/features/models/model_list_view_model.dartexamples/flutter/RunAnywhereAI/lib/features/models/model_selection_sheet.dartexamples/flutter/RunAnywhereAI/lib/features/rag/rag_demo_view.dartexamples/flutter/RunAnywhereAI/lib/features/settings/combined_settings_view.dartexamples/flutter/RunAnywhereAI/lib/features/settings/tool_settings_view_model.dartexamples/flutter/RunAnywhereAI/lib/features/structured_output/structured_output_view.dartexamples/flutter/RunAnywhereAI/lib/features/vision/services/vision_camera_backend.dartexamples/flutter/RunAnywhereAI/lib/features/vision/services/vision_permission_gateway.dartexamples/flutter/RunAnywhereAI/lib/features/vision/services/vision_vlm_service.dartexamples/flutter/RunAnywhereAI/lib/features/vision/vision_hub_view.dartexamples/flutter/RunAnywhereAI/lib/features/vision/vlm_camera_view.dartexamples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dartexamples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dartexamples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dartexamples/flutter/RunAnywhereAI/lib/features/voice/voice_assistant_view.dartexamples/flutter/RunAnywhereAI/pubspec.yamlexamples/flutter/RunAnywhereAI/test/core/models/proxy_settings_test.dartexamples/flutter/RunAnywhereAI/test/core/services/example_http_service_test.dartexamples/flutter/RunAnywhereAI/test/core/services/proxy_settings_service_test.dartexamples/flutter/RunAnywhereAI/test/features/dropdown_form_field_compatibility_test.dartexamples/flutter/RunAnywhereAI/test/features/vision/vlm_view_model_test.dartexamples/flutter/RunAnywhereAI/test/widget_test.dartexamples/flutter/RunAnywhereAI/windows/.gitignoreexamples/flutter/RunAnywhereAI/windows/CMakeLists.txtexamples/flutter/RunAnywhereAI/windows/flutter/CMakeLists.txtexamples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.ccexamples/flutter/RunAnywhereAI/windows/flutter/generated_plugin_registrant.hexamples/flutter/RunAnywhereAI/windows/flutter/generated_plugins.cmakeexamples/flutter/RunAnywhereAI/windows/runner/CMakeLists.txtexamples/flutter/RunAnywhereAI/windows/runner/Runner.rcexamples/flutter/RunAnywhereAI/windows/runner/flutter_window.cppexamples/flutter/RunAnywhereAI/windows/runner/flutter_window.hexamples/flutter/RunAnywhereAI/windows/runner/main.cppexamples/flutter/RunAnywhereAI/windows/runner/resource.hexamples/flutter/RunAnywhereAI/windows/runner/runner.exe.manifestexamples/flutter/RunAnywhereAI/windows/runner/utils.cppexamples/flutter/RunAnywhereAI/windows/runner/utils.hexamples/flutter/RunAnywhereAI/windows/runner/win32_window.cppexamples/flutter/RunAnywhereAI/windows/runner/win32_window.hsdk/runanywhere-commons/CMakeLists.txtsdk/runanywhere-commons/README.mdsdk/runanywhere-commons/scripts/build-windows.batsdk/runanywhere-commons/scripts/windows/download-sherpa-onnx.batsdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txtsdk/runanywhere-commons/src/backends/onnx/CMakeLists.txtsdk/runanywhere-commons/src/backends/onnx/onnx_backend.cppsdk/runanywhere-commons/src/backends/onnx/rac_backend_onnx_register.cppsdk/runanywhere-commons/src/core/rac_core.cppsdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cppsdk/runanywhere-commons/src/infrastructure/extraction/rac_extraction_stub.cppsdk/runanywhere-commons/tests/test_download_orchestrator.cppsdk/runanywhere-flutter/packages/runanywhere/README.mdsdk/runanywhere-flutter/packages/runanywhere/lib/infrastructure/download/download_service.dartsdk/runanywhere-flutter/packages/runanywhere/lib/internal/vlm_file_resolution.dartsdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_auth.dartsdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_model_registry.dartsdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_stt.dartsdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_tts.dartsdk/runanywhere-flutter/packages/runanywhere/lib/native/platform_loader.dartsdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dartsdk/runanywhere-flutter/packages/runanywhere/pubspec.yamlsdk/runanywhere-flutter/packages/runanywhere/test/auth_sdk_config_ffi_layout_test.dartsdk/runanywhere-flutter/packages/runanywhere/test/dart_bridge_auth_test.dartsdk/runanywhere-flutter/packages/runanywhere/test/download_service_test.dartsdk/runanywhere-flutter/packages/runanywhere/test/vlm_file_resolution_test.dartsdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txtsdk/runanywhere-flutter/packages/runanywhere/windows/include/runanywhere/runanywhere_plugin_c_api.hsdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.cppsdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.hsdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin_c_api.cppsdk/runanywhere-flutter/packages/runanywhere_llamacpp/lib/native/llamacpp_bindings.dartsdk/runanywhere-flutter/packages/runanywhere_llamacpp/pubspec.yamlsdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/CMakeLists.txtsdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/include/runanywhere_llamacpp/runanywhere_llamacpp_plugin_c_api.hsdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cppsdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.hsdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin_c_api.cppsdk/runanywhere-flutter/packages/runanywhere_onnx/lib/native/onnx_bindings.dartsdk/runanywhere-flutter/packages/runanywhere_onnx/pubspec.yamlsdk/runanywhere-flutter/packages/runanywhere_onnx/windows/CMakeLists.txtsdk/runanywhere-flutter/packages/runanywhere_onnx/windows/include/runanywhere_onnx/runanywhere_onnx_plugin_c_api.hsdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.cppsdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.hsdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin_c_api.cpp
💤 Files with no reviewable changes (1)
- examples/flutter/RunAnywhereAI/.gitignore
✅ Files skipped from review due to trivial changes (24)
- .gitignore
- sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_tts.dart
- examples/flutter/RunAnywhereAI/windows/runner/runner.exe.manifest
- examples/flutter/RunAnywhereAI/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
- examples/flutter/RunAnywhereAI/windows/.gitignore
- sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_stt.dart
- examples/flutter/RunAnywhereAI/lib/features/structured_output/structured_output_view.dart
- examples/flutter/RunAnywhereAI/test/core/models/proxy_settings_test.dart
- examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_permission_gateway.dart
- examples/flutter/RunAnywhereAI/windows/runner/resource.h
- examples/flutter/RunAnywhereAI/lib/core/design_system/unsupported_feature_view.dart
- sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_model_registry.dart
- sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin_c_api.cpp
- sdk/runanywhere-flutter/packages/runanywhere_llamacpp/pubspec.yaml
- sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin_c_api.cpp
- examples/flutter/RunAnywhereAI/windows/runner/Runner.rc
- sdk/runanywhere-commons/README.md
- docs/building.md
- examples/flutter/RunAnywhereAI/test/features/dropdown_form_field_compatibility_test.dart
- examples/flutter/RunAnywhereAI/lib/core/utilities/constants.dart
- examples/flutter/RunAnywhereAI/windows/flutter/generated_plugins.cmake
- examples/flutter/RunAnywhereAI/lib/features/models/model_selection_sheet.dart
- examples/flutter/RunAnywhereAI/windows/runner/CMakeLists.txt
- sdk/runanywhere-commons/src/infrastructure/download/download_orchestrator.cpp
🚧 Files skipped from review as they are similar to previous changes (33)
- sdk/runanywhere-flutter/packages/runanywhere/pubspec.yaml
- sdk/runanywhere-commons/CMakeLists.txt
- sdk/runanywhere-flutter/packages/runanywhere_onnx/pubspec.yaml
- sdk/runanywhere-flutter/packages/runanywhere/test/auth_sdk_config_ffi_layout_test.dart
- examples/flutter/RunAnywhereAI/lib/features/models/add_model_from_url_view.dart
- sdk/runanywhere-flutter/packages/runanywhere_llamacpp/lib/native/llamacpp_bindings.dart
- examples/flutter/RunAnywhereAI/test/core/services/example_http_service_test.dart
- examples/flutter/RunAnywhereAI/pubspec.yaml
- examples/flutter/RunAnywhereAI/test/widget_test.dart
- sdk/runanywhere-flutter/packages/runanywhere/README.md
- sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/runanywhere_llamacpp_plugin.cpp
- sdk/runanywhere-commons/tests/test_download_orchestrator.cpp
- sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin_c_api.cpp
- sdk/runanywhere-flutter/packages/runanywhere/test/vlm_file_resolution_test.dart
- sdk/runanywhere-flutter/packages/runanywhere/test/download_service_test.dart
- examples/flutter/RunAnywhereAI/test/core/services/proxy_settings_service_test.dart
- sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/runanywhere_onnx_plugin.cpp
- examples/flutter/RunAnywhereAI/lib/core/design_system/app_colors.dart
- sdk/runanywhere-commons/src/backends/onnx/CMakeLists.txt
- examples/flutter/RunAnywhereAI/lib/features/vision/services/vision_vlm_service.dart
- examples/flutter/RunAnywhereAI/lib/features/voice/voice_assistant_view.dart
- examples/flutter/RunAnywhereAI/windows/runner/utils.cpp
- examples/flutter/RunAnywhereAI/test/features/vision/vlm_view_model_test.dart
- sdk/runanywhere-flutter/packages/runanywhere/windows/runanywhere_plugin.cpp
- sdk/runanywhere-commons/src/backends/onnx/rac_backend_onnx_register.cpp
- examples/flutter/RunAnywhereAI/lib/features/voice/text_to_speech_view.dart
- sdk/runanywhere-commons/src/core/rac_core.cpp
- examples/flutter/RunAnywhereAI/lib/core/models/proxy_settings.dart
- sdk/runanywhere-flutter/packages/runanywhere_llamacpp/windows/CMakeLists.txt
- sdk/runanywhere-flutter/packages/runanywhere/lib/public/runanywhere.dart
- examples/flutter/RunAnywhereAI/lib/features/vision/vlm_camera_view.dart
- examples/flutter/RunAnywhereAI/windows/flutter/CMakeLists.txt
- examples/flutter/RunAnywhereAI/lib/features/chat/chat_interface_view.dart
| debugPrint('✅ ONNX backend registered (Windows optional)'); | ||
| } catch (e) { | ||
| debugPrint('⚠️ ONNX backend not available: $e'); | ||
| debugPrint('⚠️ ONNX backend not available on this Windows build: $e'); | ||
| } | ||
|
|
||
| // --- RAG BACKEND --- | ||
| try { | ||
| await RAGModule.register(); | ||
| debugPrint('✅ RAG backend registered'); | ||
| } catch (e) { | ||
| debugPrint('⚠️ RAG backend not available (RAG features disabled): $e'); | ||
| debugPrint('⚠️ RAG backend not available on this Windows build: $e'); | ||
| } |
There was a problem hiding this comment.
Platform-specific wording in log messages will be misleading on non-Windows builds.
These debugPrint strings hard-code "Windows" even though _registerModulesAndModels runs on every target platform (iOS/Android/macOS/Linux/Windows). If Onnx.register() or RAGModule.register() fails on, say, Linux or Android, the log will incorrectly say "not available on this Windows build", which will make production/CI debugging harder. Similarly, "ONNX backend registered (Windows optional)" on Line 367 is confusing in the success path on every other platform.
Consider either platform-gating these messages or making them platform-agnostic.
📝 Suggested rewording
try {
await Onnx.register();
- debugPrint('✅ ONNX backend registered (Windows optional)');
+ debugPrint('✅ ONNX backend registered');
} catch (e) {
- debugPrint('⚠️ ONNX backend not available on this Windows build: $e');
+ debugPrint('⚠️ ONNX backend not available on this platform: $e');
}
// --- RAG BACKEND ---
try {
await RAGModule.register();
debugPrint('✅ RAG backend registered');
} catch (e) {
- debugPrint('⚠️ RAG backend not available on this Windows build: $e');
+ debugPrint('⚠️ RAG backend not available on this platform: $e');
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/flutter/RunAnywhereAI/lib/app/runanywhere_ai_app.dart` around lines
367 - 378, Log messages in _registerModulesAndModels hard-code "Windows" causing
misleading output when Onnx.register() or RAGModule.register() runs on other
OSes; update the catch and success debugPrints for Onnx.register() and
RAGModule.register() to be platform-agnostic (or gate them behind a
Platform.isWindows check) and include actual platform info when available (e.g.,
use Platform.operatingSystem) so success messages like "ONNX backend registered"
and failure messages do not incorrectly state "on this Windows build."
| Future<void> initializeCamera() async { | ||
| try { | ||
| final cameras = await availableCameras(); | ||
| if (cameras.isEmpty) { | ||
| debugPrint('❌ No cameras available'); | ||
| final devices = await _cameraBackend.listDevices(); | ||
| if (_isDisposed) { | ||
| return; | ||
| } | ||
| if (devices.isEmpty) { | ||
| _hasCameraDevice = false; | ||
| _isCameraInitialized = false; | ||
| _error = 'No cameras available on this device.'; | ||
| _safeNotifyListeners(); | ||
| return; | ||
| } | ||
|
|
||
| // Select back camera (or first available) | ||
| final camera = cameras.firstWhere( | ||
| (c) => c.lensDirection == CameraLensDirection.back, | ||
| orElse: () => cameras.first, | ||
| ); | ||
|
|
||
| // Create controller with BGRA format request (iOS preferred, Android fallback to YUV) | ||
| _cameraController = CameraController( | ||
| camera, | ||
| ResolutionPreset.medium, | ||
| imageFormatGroup: ImageFormatGroup.bgra8888, | ||
| _hasCameraDevice = true; | ||
| final device = devices.firstWhere( | ||
| (camera) => camera.lensDirection == VisionCameraLensDirection.back, | ||
| orElse: () => devices.first, | ||
| ); | ||
|
|
||
| await _cameraController!.initialize(); | ||
| await _cameraSession?.dispose(); | ||
| if (_isDisposed) { | ||
| return; | ||
| } | ||
| final nextSession = _cameraBackend.createSession(device); | ||
| if (_isDisposed) { | ||
| unawaited(nextSession.dispose()); | ||
| return; | ||
| } | ||
| try { | ||
| await nextSession.initialize(); | ||
| } catch (_) { | ||
| await nextSession.dispose(); | ||
| rethrow; | ||
| } | ||
| if (_isDisposed) { | ||
| unawaited(nextSession.dispose()); | ||
| return; | ||
| } | ||
| _cameraSession = nextSession; | ||
|
|
||
| _isCameraInitialized = true; | ||
| notifyListeners(); | ||
|
|
||
| debugPrint('✅ Camera initialized: ${camera.lensDirection}'); | ||
| _error = null; | ||
| _safeNotifyListeners(); | ||
| } catch (e) { | ||
| debugPrint('❌ Camera initialization failed: $e'); | ||
| if (_isDisposed) { | ||
| return; | ||
| } | ||
| _isCameraInitialized = false; | ||
| _error = 'Failed to initialize camera: $e'; | ||
| notifyListeners(); | ||
| _safeNotifyListeners(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Race in initializeCamera: concurrent calls can orphan camera sessions.
If initializeCamera() is re-entered (e.g., rapid retry after permission grant, hot-reload, or UI re-mount) while a prior call is still awaiting listDevices() or nextSession.initialize():
- Call A builds
nextSessionAbut hasn't yet reached the_cameraSession = nextSessionassignment at line 95. - Call B's
await _cameraSession?.dispose()at line 76 sees_cameraSession == nulland does nothing. - Both calls create and initialize their own sessions. Whichever assigns last wins; the other
nextSessionis leaked — itsCameraControlleris never disposed, holding the OS camera handle open.
On iOS and especially Windows this will commonly manifest as a subsequent "camera in use" error. Guard the method against reentry or supersede in-flight initialization.
🔒 Proposed guard
bool _hasCameraDevice = true;
bool _isDisposed = false;
+ int _cameraInitToken = 0;
VisionCameraSession? _cameraSession;
Timer? _autoStreamTimer;
@@
Future<void> initializeCamera() async {
+ final token = ++_cameraInitToken;
try {
final devices = await _cameraBackend.listDevices();
- if (_isDisposed) {
+ if (_isDisposed || token != _cameraInitToken) {
return;
}
@@
- await _cameraSession?.dispose();
- if (_isDisposed) {
- return;
- }
final nextSession = _cameraBackend.createSession(device);
- if (_isDisposed) {
+ if (_isDisposed || token != _cameraInitToken) {
unawaited(nextSession.dispose());
return;
}
try {
await nextSession.initialize();
} catch (_) {
await nextSession.dispose();
rethrow;
}
- if (_isDisposed) {
+ if (_isDisposed || token != _cameraInitToken) {
unawaited(nextSession.dispose());
return;
}
+ final previous = _cameraSession;
_cameraSession = nextSession;
+ unawaited(previous?.dispose());🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart`
around lines 56 - 108, initializeCamera can be re-entered and leak
CameraSessions; introduce a reentrancy guard (e.g., a private field like
_cameraInitToken or bool _isInitializingCamera) and use it to serialize or
supersede in-flight initializations: when starting initializeCamera() capture a
new token (or set _isInitializingCamera) and store it locally; before
creating/initializing sessions check that the current token still matches (or
_isInitializingCamera is still yours), and if not dispose any created
nextSession immediately and return; when assigning _cameraSession, dispose the
previous _cameraSession first and then set _cameraSession = nextSession only if
the token still matches; clear/reset the guard (_isInitializingCamera or token)
on normal completion or error, and ensure all early-return paths dispose newly
created sessions to avoid orphaning.
| Future<void> describePickedImage(String imagePath) async { | ||
| if (_isDisposed) { | ||
| return; | ||
| } | ||
| _isProcessing = true; | ||
| _error = null; | ||
| _currentDescription = ''; | ||
| notifyListeners(); | ||
| _safeNotifyListeners(); | ||
|
|
||
| try { | ||
| // Create VLMImage from file path | ||
| final image = sdk.VLMImage.filePath(imagePath); | ||
|
|
||
| // Process image with streaming (more detailed prompt) | ||
| final result = await sdk.RunAnywhere.processImageStream( | ||
| image, | ||
| final tokens = _vlmService.processImageStream( | ||
| imagePath, | ||
| prompt: 'Describe this image in detail.', | ||
| options: const sdk.VLMGenerationOptions(maxTokens: 300), | ||
| maxTokens: 300, | ||
| ); | ||
|
|
||
| // Listen to stream and append tokens | ||
| final buffer = StringBuffer(_currentDescription); | ||
| await for (final token in result.stream) { | ||
| final buffer = StringBuffer(); | ||
| await for (final token in tokens) { | ||
| if (_isDisposed) { | ||
| return; | ||
| } | ||
| buffer.write(token); | ||
| _currentDescription = buffer.toString(); | ||
| notifyListeners(); | ||
| _safeNotifyListeners(); | ||
| } | ||
|
|
||
| debugPrint('✅ Gallery photo described: ${_currentDescription.length} chars'); | ||
| } catch (e) { | ||
| debugPrint('❌ Gallery photo error: $e'); | ||
| if (_isDisposed) { | ||
| return; | ||
| } | ||
| _error = e.toString(); | ||
| notifyListeners(); | ||
| _safeNotifyListeners(); | ||
| } finally { | ||
| _isProcessing = false; | ||
| notifyListeners(); | ||
| _safeNotifyListeners(); | ||
| } | ||
| } |
There was a problem hiding this comment.
describePickedImage lacks an _isProcessing guard.
Unlike describeCurrentFrame (line 154) and _describeCurrentFrameForAutoStream (line 256), this path will start a second VLM stream while one is already in flight (e.g., a user picks a new image while auto-streaming or while a previous description is still streaming). Both streams then interleave writes into _currentDescription, producing garbled output, and only one _isProcessing = false in the last finally wins.
Consider gating the entry and/or cancelling in-flight generation first:
🛡️ Proposed guard
Future<void> describePickedImage(String imagePath) async {
- if (_isDisposed) {
+ if (_isDisposed || _isProcessing) {
return;
}📝 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.
| Future<void> describePickedImage(String imagePath) async { | |
| if (_isDisposed) { | |
| return; | |
| } | |
| _isProcessing = true; | |
| _error = null; | |
| _currentDescription = ''; | |
| notifyListeners(); | |
| _safeNotifyListeners(); | |
| try { | |
| // Create VLMImage from file path | |
| final image = sdk.VLMImage.filePath(imagePath); | |
| // Process image with streaming (more detailed prompt) | |
| final result = await sdk.RunAnywhere.processImageStream( | |
| image, | |
| final tokens = _vlmService.processImageStream( | |
| imagePath, | |
| prompt: 'Describe this image in detail.', | |
| options: const sdk.VLMGenerationOptions(maxTokens: 300), | |
| maxTokens: 300, | |
| ); | |
| // Listen to stream and append tokens | |
| final buffer = StringBuffer(_currentDescription); | |
| await for (final token in result.stream) { | |
| final buffer = StringBuffer(); | |
| await for (final token in tokens) { | |
| if (_isDisposed) { | |
| return; | |
| } | |
| buffer.write(token); | |
| _currentDescription = buffer.toString(); | |
| notifyListeners(); | |
| _safeNotifyListeners(); | |
| } | |
| debugPrint('✅ Gallery photo described: ${_currentDescription.length} chars'); | |
| } catch (e) { | |
| debugPrint('❌ Gallery photo error: $e'); | |
| if (_isDisposed) { | |
| return; | |
| } | |
| _error = e.toString(); | |
| notifyListeners(); | |
| _safeNotifyListeners(); | |
| } finally { | |
| _isProcessing = false; | |
| notifyListeners(); | |
| _safeNotifyListeners(); | |
| } | |
| } | |
| Future<void> describePickedImage(String imagePath) async { | |
| if (_isDisposed || _isProcessing) { | |
| return; | |
| } | |
| _isProcessing = true; | |
| _error = null; | |
| _currentDescription = ''; | |
| _safeNotifyListeners(); | |
| try { | |
| final tokens = _vlmService.processImageStream( | |
| imagePath, | |
| prompt: 'Describe this image in detail.', | |
| maxTokens: 300, | |
| ); | |
| final buffer = StringBuffer(); | |
| await for (final token in tokens) { | |
| if (_isDisposed) { | |
| return; | |
| } | |
| buffer.write(token); | |
| _currentDescription = buffer.toString(); | |
| _safeNotifyListeners(); | |
| } | |
| } catch (e) { | |
| if (_isDisposed) { | |
| return; | |
| } | |
| _error = e.toString(); | |
| _safeNotifyListeners(); | |
| } finally { | |
| _isProcessing = false; | |
| _safeNotifyListeners(); | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/flutter/RunAnywhereAI/lib/features/vision/vlm_view_model.dart`
around lines 195 - 230, describePickedImage can start a new VLM stream while
another is active, causing interleaved writes to _currentDescription; add an
entry guard and/or cancel the in-flight generation before starting: at the top
of describePickedImage check _isProcessing and return early (like
describeCurrentFrame), or call the same cancellation/stop routine used by
_describeCurrentFrameForAutoStream to abort the existing stream before creating
tokens; ensure any canceled stream sets _isProcessing to false and that
describePickedImage sets _isProcessing = true only after the previous stream is
confirmed stopped to avoid concurrent writes to _currentDescription.
| Future<void> _refreshModelState() async { | ||
| final currentModel = await sdk.RunAnywhere.currentSTTModel(); | ||
| if (!mounted) return; | ||
|
|
||
| setState(() { | ||
| _isProcessing = true; | ||
| _selectedFramework = currentModel == null | ||
| ? LLMFramework.unknown | ||
| : switch (currentModel.framework) { | ||
| sdk.InferenceFramework.onnx => LLMFramework.onnxRuntime, | ||
| _ => LLMFramework.unknown, | ||
| }; | ||
| _selectedModelName = currentModel?.name; | ||
| _supportsLiveMode = false; | ||
| _errorMessage = null; | ||
| }); | ||
|
|
||
| try { | ||
| debugPrint('🔄 Loading STT model: ${model.name}'); | ||
|
|
||
| // Load STT model directly via SDK (matches Swift: RunAnywhere.loadSTTModel) | ||
| await sdk.RunAnywhere.loadSTTModel(model.id); | ||
|
|
||
| setState(() { | ||
| _selectedFramework = | ||
| model.preferredFramework ?? LLMFramework.whisperKit; | ||
| _selectedModelName = model.name; | ||
| // WhisperKit supports live mode, ONNX may have limitations | ||
| _supportsLiveMode = model.preferredFramework == LLMFramework.whisperKit; | ||
| _isProcessing = false; | ||
| }); | ||
|
|
||
| debugPrint('✅ STT model loaded: ${model.name}'); | ||
| } catch (e) { | ||
| debugPrint('❌ Failed to load STT model: $e'); | ||
| setState(() { | ||
| _errorMessage = 'Failed to load model: $e'; | ||
| _isProcessing = false; | ||
| }); | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# List InferenceFramework enum cases and how STT models are tagged.
ast-grep --pattern 'enum InferenceFramework { $$$ }'
rg -nP --type=dart -C2 '\bInferenceFramework\.' sdk/runanywhere-flutter | head -n 80
# Find STT model registrations to see which frameworks they use.
rg -nP --type=dart -C3 'ModelCategory\.stt|category:\s*ModelCategory\.stt' | head -n 60Repository: RunanywhereAI/runanywhere-sdks
Length of output: 13584
🏁 Script executed:
# Find STT model definitions and their frameworks
rg -nP --type=dart 'ModelCategory\.stt|\.stt\b' --max-count=50Repository: RunanywhereAI/runanywhere-sdks
Length of output: 4081
🏁 Script executed:
# Find LLMFramework enum definition
fd -e dart -x rg -l 'enum LLMFramework' {}Repository: RunanywhereAI/runanywhere-sdks
Length of output: 144
🏁 Script executed:
# Check git history for the _refreshModelState method to see prior implementation
git log -p --all -S '_refreshModelState' -- '*.dart' | head -200Repository: RunanywhereAI/runanywhere-sdks
Length of output: 56
🏁 Script executed:
# Search for _supportsLiveMode usage to understand its impact
rg -nP --type=dart '_supportsLiveMode' -B 2 -A 2Repository: RunanywhereAI/runanywhere-sdks
Length of output: 1685
🏁 Script executed:
# Read the LLMFramework enum definition
cat -n examples/flutter/RunAnywhereAI/lib/features/models/model_types.dart | head -100Repository: RunanywhereAI/runanywhere-sdks
Length of output: 3144
🏁 Script executed:
# Search for STT model definitions with framework values
rg -nP --type=dart '(ModelSelectionContext\.stt|category.*stt)' -B 5 -A 10 examples/flutter/RunAnywhereAI | head -150Repository: RunanywhereAI/runanywhere-sdks
Length of output: 14747
🏁 Script executed:
# Look for model registrations that might include STT models
rg -nP --type=dart 'framework:.*InferenceFramework' -B 2 -A 2 | head -100Repository: RunanywhereAI/runanywhere-sdks
Length of output: 8283
🏁 Script executed:
# Search for foundationModels in the context of speech/STT
rg -nP --type=dart 'foundationModels' -B 3 -A 3 | head -80Repository: RunanywhereAI/runanywhere-sdks
Length of output: 9377
🏁 Script executed:
# Search for whiskerKit/whisper framework references
rg -nP --type=dart -i 'whisper' | head -50Repository: RunanywhereAI/runanywhere-sdks
Length of output: 5379
🏁 Script executed:
# Check if there are model definitions specifically mapping STT to frameworks
fd -e dart -x grep -l 'speechRecognition\|stt' {} \; | head -10 | xargs -I {} sh -c 'echo "=== {} ===" && cat -n {} | grep -A 5 -B 5 framework'Repository: RunanywhereAI/runanywhere-sdks
Length of output: 15905
Extend the framework switch to handle all possible STT frameworks, not just ONNX.
The switch maps only sdk.InferenceFramework.onnx → LLMFramework.onnxRuntime; other frameworks like foundationModels and systemTTS (which exist in the SDK enum) fall through to LLMFramework.unknown. The chat interface uses a more complete pattern—add those mappings here as well. Follow the pattern in chat_interface_view.dart (lines 557–565) which handles llamaCpp, foundationModels, systemTTS, etc.
Also, _supportsLiveMode is unconditionally set to false regardless of the loaded model's capabilities. Either derive this from the model/framework or add a comment explaining why batch-only mode is required.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@examples/flutter/RunAnywhereAI/lib/features/voice/speech_to_text_view.dart`
around lines 123 - 138, Update _refreshModelState to handle all
sdk.InferenceFramework cases instead of only onnx: in the switch over
currentModel.framework (inside _refreshModelState) add branches for
sdk.InferenceFramework.llamaCpp → LLMFramework.llamaCpp,
sdk.InferenceFramework.foundationModels → LLMFramework.foundationModels,
sdk.InferenceFramework.systemTTS → LLMFramework.systemTTS (and any other enum
members present) following the mapping pattern used in chat_interface_view.dart;
do not leave other frameworks falling back to unknown. Also stop unconditionally
setting _supportsLiveMode = false: derive it from the model if a capability flag
exists (e.g., currentModel.supportsLiveMode or currentModel.capabilities) or, if
no such field exists, add a short comment above the assignment explaining why
this view is batch-only and keep the code consistent with
_selectedFramework/_selectedModelName handling in _refreshModelState.
| elseif(RAC_PLATFORM_WINDOWS) | ||
| message(STATUS "Configuring LlamaCPP backend for Windows") | ||
| target_compile_definitions(rac_backend_llamacpp PRIVATE NOMINMAX WIN32_LEAN_AND_MEAN) | ||
| target_link_libraries(rac_backend_llamacpp PUBLIC ws2_32) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check whether any public header of rac_backend_llamacpp references winsock APIs/types,
# which would justify PUBLIC linkage of ws2_32.
fd -e h -e hpp . sdk/runanywhere-commons/src/backends/llamacpp | xargs rg -n -C2 -P '\b(winsock2\.h|ws2tcpip\.h|WSAStartup|SOCKET|sockaddr_in|htons|htonl|ntohs|ntohl)\b' || echo "No winsock references found in llamacpp backend headers."Repository: RunanywhereAI/runanywhere-sdks
Length of output: 131
🏁 Script executed:
# 1. Find the public headers for rac_backend_llamacpp
find sdk/runanywhere-commons/include/rac -name "*llamacpp*" -o -name "*backend*" | head -20Repository: RunanywhereAI/runanywhere-sdks
Length of output: 314
🏁 Script executed:
# 2. Check the CMakeLists.txt file for context around line 287
sed -n '280,295p' sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txtRepository: RunanywhereAI/runanywhere-sdks
Length of output: 666
🏁 Script executed:
# 3. Search for actual ws2_32 usage in the llamacpp backend source
rg -n 'ws2_32|winsock|WSA' sdk/runanywhere-commons/src/backends/llamacpp --type cpp --type cc --type hRepository: RunanywhereAI/runanywhere-sdks
Length of output: 101
🏁 Script executed:
# 4. Check if there are any public header files for llamacpp backend
find sdk/runanywhere-commons/include -name "*.h" -o -name "*.hpp" | xargs rg -l "llamacpp" 2>/dev/null || echo "No llamacpp references in public headers"Repository: RunanywhereAI/runanywhere-sdks
Length of output: 339
🏁 Script executed:
# Check the public headers for any winsock/network-related symbols
cat -n sdk/runanywhere-commons/include/rac/backends/rac_llm_llamacpp.hRepository: RunanywhereAI/runanywhere-sdks
Length of output: 14244
🏁 Script executed:
cat -n sdk/runanywhere-commons/include/rac/backends/rac_vlm_llamacpp.hRepository: RunanywhereAI/runanywhere-sdks
Length of output: 8903
🏁 Script executed:
# Try searching for ws2_32 usage in source files with correct type
rg -n 'ws2_32|winsock|WSA' sdk/runanywhere-commons/src/backends/llamacppRepository: RunanywhereAI/runanywhere-sdks
Length of output: 201
🏁 Script executed:
# Check if the backend actually needs ws2_32 by looking at implementation files
find sdk/runanywhere-commons/src/backends/llamacpp -type f \( -name "*.cpp" -o -name "*.h" -o -name "*.hpp" \) | head -20Repository: RunanywhereAI/runanywhere-sdks
Length of output: 583
🏁 Script executed:
# Search for actual winsock usage in implementation files
rg -n 'winsock|WSA|ws2_32|SOCKET|sockaddr|htons|ntohs' sdk/runanywhere-commons/src/backends/llamacpp --type-add 'cpp:*.{cpp,h,hpp}' -t cppRepository: RunanywhereAI/runanywhere-sdks
Length of output: 56
🏁 Script executed:
# Also check if llamacpp library itself might need it (check external dependencies)
grep -r "ws2_32\|winsock\|WSA" sdk/runanywhere-commons/src/backends/llamacpp/ 2>/dev/null | head -20Repository: RunanywhereAI/runanywhere-sdks
Length of output: 197
Change ws2_32 linking to PRIVATE.
No winsock APIs or types are exposed in the public headers (rac_llm_llamacpp.h, rac_vlm_llamacpp.h), and no winsock usage was found in the backend implementation. Since ws2_32 is a pure implementation detail, linking it as PUBLIC unnecessarily propagates the Windows-only dependency to all transitive consumers. Change to PRIVATE to avoid leaking this platform-specific requirement.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sdk/runanywhere-commons/src/backends/llamacpp/CMakeLists.txt` at line 287,
Change the CMake target_link_libraries usage so the Windows sockets lib is not
propagated: in the CMakeLists where target_link_libraries(rac_backend_llamacpp
PUBLIC ws2_32) is used, change the link scope from PUBLIC to PRIVATE (i.e., make
ws2_32 a private dependency of the rac_backend_llamacpp target) so the
Windows-only ws2_32 symbol is not exposed to transitive consumers.
| get_filename_component(PACKAGE_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/.." ABSOLUTE) | ||
| get_filename_component(REPO_ROOT_FROM_APP "${CMAKE_SOURCE_DIR}/../../../../" ABSOLUTE) | ||
| set(ONNX_DIST_DIR_CANDIDATES | ||
| "${REPO_ROOT_FROM_APP}/sdk/runanywhere-commons/dist/windows/x64" | ||
| "${PACKAGE_ROOT}/../../../runanywhere-commons/dist/windows/x64" | ||
| ) | ||
|
|
||
| set(ONNX_DIST_DIR "") | ||
| foreach(candidate ${ONNX_DIST_DIR_CANDIDATES}) | ||
| if(EXISTS "${candidate}") | ||
| set(ONNX_DIST_DIR "${candidate}") | ||
| break() | ||
| endif() | ||
| endforeach() |
There was a problem hiding this comment.
Fallback path only works for the bundled example app.
REPO_ROOT_FROM_APP assumes the consumer sits exactly four levels deep under the repo root (matches examples/flutter/RunAnywhereAI/windows/). The PACKAGE_ROOT/../../.. candidate similarly assumes the package lives at sdk/runanywhere-flutter/packages/runanywhere_onnx on disk — which won't hold when the plugin is consumed from pub cache or a symlinked checkout elsewhere. For third-party apps this silently falls through to the "ONNX dist directory not found" warning and ships the app with no ONNX runtime.
Consider documenting an override like -DRAC_ONNX_DIST_DIR=<path> and honoring it as the first candidate so external integrators have a reliable override.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/CMakeLists.txt`
around lines 33 - 46, Add a configurable override for the ONNX distribution path
and check it before the hardcoded candidates: declare and use a CMake cache
variable (e.g. RAC_ONNX_DIST_DIR) and, if set and EXISTS, set ONNX_DIST_DIR to
that value before iterating ONNX_DIST_DIR_CANDIDATES; keep existing PACKAGE_ROOT
and REPO_ROOT_FROM_APP candidates as fallbacks and preserve the final "not
found" warning behavior so external consumers (pub cache or symlinked checkouts)
can reliably pass -DRAC_ONNX_DIST_DIR=<path>.
| if(EXISTS "${ONNX_DIST_DIR}") | ||
| file(GLOB ONNX_DIST_DLLS "${ONNX_DIST_DIR}/*.dll") | ||
| set(ONNX_RUNTIME_DLLS "") | ||
|
|
||
| foreach(candidate ${ONNX_DIST_DLLS}) | ||
| get_filename_component(candidate_name "${candidate}" NAME) | ||
| if(candidate_name STREQUAL "rac_backend_onnx.dll") | ||
| list(APPEND ONNX_RUNTIME_DLLS "${candidate}") | ||
| elseif(candidate_name MATCHES "^onnxruntime.*\\.dll$") | ||
| list(APPEND ONNX_RUNTIME_DLLS "${candidate}") | ||
| elseif(candidate_name MATCHES "^sherpa-onnx.*\\.dll$") | ||
| list(APPEND ONNX_RUNTIME_DLLS "${candidate}") | ||
| elseif(candidate_name STREQUAL "cargs.dll") | ||
| list(APPEND ONNX_RUNTIME_DLLS "${candidate}") | ||
| endif() | ||
| endforeach() |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Locate any Windows CMakeLists for the core runanywhere plugin and check for rac_commons.dll bundling.
fd -t f CMakeLists.txt sdk/runanywhere-flutter/packages/runanywhere/windows
rg -nP --type=cmake -C3 '\brac_commons(\.dll)?\b'
# Check the example app's Windows tree for explicit copies.
rg -nP -C3 '\brac_commons\.dll\b' examples/flutter/RunAnywhereAI/windowsRepository: RunanywhereAI/runanywhere-sdks
Length of output: 40523
🏁 Script executed:
# Check the ONNX plugin's pubspec.yaml for dependencies
fd -t f pubspec.yaml sdk/runanywhere-flutter/packages/runanywhere_onnx | head -1 | xargs cat
# Also check the core plugin's full Windows CMakeLists to see how bundled_libraries is exported
head -80 sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txtRepository: RunanywhereAI/runanywhere-sdks
Length of output: 3041
🏁 Script executed:
# Get full core plugin Windows CMakeLists
cat -n sdk/runanywhere-flutter/packages/runanywhere/windows/CMakeLists.txt
# Get full ONNX plugin Windows CMakeLists
cat -n sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/CMakeLists.txtRepository: RunanywhereAI/runanywhere-sdks
Length of output: 5897
rac_commons.dll dependency not explicitly bundled by ONNX plugin; relies on transitive propagation from core plugin.
The review raised concerns about the DLL allowlist and missing-dist handling. Verification shows:
-
Missing-dist error handling is less defensive here than in the core plugin. The core
runanywhereplugin (which ONNX depends on per pubspec.yaml) uses aFATAL_ERRORto fail the build ifrac_commons.dllis missing. The ONNX plugin instead uses aWARNINGand silently continues, making it easier to accidentally ship builds without ONNX support. -
rac_commons.dllis not in the ONNX allowlist. The allowlist filters forrac_backend_onnx.dll,onnxruntime*.dll,sherpa-onnx*.dll, andcargs.dllonly. The core plugin bundlesrac_commons.dllseparately, so the ONNX plugin implicitly relies on Flutter's plugin dependency system to propagate it. This is fragile—if the app omits the core plugin (unlikely, but possible) or if the dependency propagation fails, the runtime will fail with a missing-library error when loadingrac_backend_onnx.dll.
Recommendation: Either explicitly include rac_commons.dll in the allowlist as a defensive measure, or add a comment documenting that this plugin depends on the core runanywhere plugin to provide rac_commons.dll at runtime. Also consider using FATAL_ERROR instead of WARNING when the dist directory is missing, to match the core plugin's safer behavior.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sdk/runanywhere-flutter/packages/runanywhere_onnx/windows/CMakeLists.txt`
around lines 48 - 63, The ONNX plugin's DLL allowlist and missing-dist handling
are fragile: add "rac_commons.dll" to the allowlist so it is bundled explicitly
(update the logic that builds ONNX_RUNTIME_DLLS to include candidate_name
STREQUAL "rac_commons.dll"), and make the missing distribution error behavior
match the core plugin by treating a missing ONNX_DIST_DIR as a fatal build error
(use FATAL_ERROR instead of WARNING) or at minimum add a clear comment in the
CMakeLists noting the runtime dependency on the core runanywhere plugin
providing rac_commons.dll; adjust the code paths that reference ONNX_DIST_DIR,
ONNX_DIST_DLLS, and ONNX_RUNTIME_DLLS accordingly.
| test('copies UTF-8 bytes into secure storage callback buffer', () { | ||
| final buffer = calloc<Uint8>(16).cast<Utf8>(); | ||
|
|
||
| try { | ||
| final written = copyUtf8StringToBufferForTesting('你好', buffer, 16); | ||
| final bytes = buffer.cast<Uint8>().asTypedList(written); | ||
|
|
||
| expect(written, utf8.encode('你好').length); | ||
| expect(bytes, utf8.encode('你好')); | ||
| expect(buffer.cast<Uint8>()[written], 0); | ||
| } finally { | ||
| calloc.free(buffer); | ||
| } | ||
| }); |
There was a problem hiding this comment.
Null-terminator assertion is masked by calloc zero-init.
Because calloc zero-fills the 16-byte buffer, buffer.cast<Uint8>()[written] == 0 would pass even if _copyUtf8StringToBuffer failed to write the terminator. Pre-fill with a non-zero sentinel so the check actually exercises the write.
🧪 Suggested tightening
test('copies UTF-8 bytes into secure storage callback buffer', () {
final buffer = calloc<Uint8>(16).cast<Utf8>();
try {
+ // Poison the buffer so a missing null terminator would surface.
+ final raw = buffer.cast<Uint8>();
+ for (var i = 0; i < 16; i++) {
+ raw[i] = 0xAA;
+ }
final written = copyUtf8StringToBufferForTesting('你好', buffer, 16);
final bytes = buffer.cast<Uint8>().asTypedList(written);
expect(written, utf8.encode('你好').length);
expect(bytes, utf8.encode('你好'));
expect(buffer.cast<Uint8>()[written], 0);
} finally {
calloc.free(buffer);
}
});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@sdk/runanywhere-flutter/packages/runanywhere/test/dart_bridge_auth_test.dart`
around lines 64 - 77, The test's null-terminator assertion is masked because
calloc zero-initializes the buffer; before calling
copyUtf8StringToBufferForTesting, overwrite the allocated buffer (buffer /
buffer.cast<Uint8>()) with a non-zero sentinel (e.g., 0xFF) so the function must
explicitly write the terminator, then call
copyUtf8StringToBufferForTesting('你好', buffer, 16) and assert the byte at index
written is 0; keep using calloc.free(buffer) in the finally block.


… or download modals
Description
Brief description of the changes made.
Type of Change
Testing
Platform-Specific Testing (check all that apply)
Swift SDK / iOS Sample:
Kotlin SDK / Android Sample:
Flutter SDK / Flutter Sample:
React Native SDK / React Native Sample:
Playground:
Web SDK / Web Sample:
Labels
Please add the appropriate label(s):
SDKs:
Swift SDK- Changes to Swift SDK (sdk/runanywhere-swift)Kotlin SDK- Changes to Kotlin SDK (sdk/runanywhere-kotlin)Flutter SDK- Changes to Flutter SDK (sdk/runanywhere-flutter)React Native SDK- Changes to React Native SDK (sdk/runanywhere-react-native)Web SDK- Changes to Web SDK (sdk/runanywhere-web)Commons- Changes to shared native code (sdk/runanywhere-commons)Sample Apps:
iOS Sample- Changes to iOS example app (examples/ios)Android Sample- Changes to Android example app (examples/android)Flutter Sample- Changes to Flutter example app (examples/flutter)React Native Sample- Changes to React Native example app (examples/react-native)Web Sample- Changes to Web example app (examples/web)Checklist
Screenshots
Attach relevant UI screenshots for changes (if applicable):
Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Note
Medium Risk
Medium risk because it adds a new Windows build target (new CMake/runner files and native DLL staging) and introduces proxy routing for HTTP/download traffic, which can affect networking and model download reliability across platforms.
Overview
Adds Windows support for the Flutter
RunAnywhereAIexample, including a newwindows/runner project,camera_windowsintegration, and updated docs/scripts for building native backends (including Sherpa-ONNX dependency download/staging) on Windows.Introduces scoped proxy support (general vs download): new proxy settings models + persistence (SharedPreferences + secure storage), a settings UI to configure/test/clear proxies, and wiring so SDK model downloads use a proxy-aware HTTP client factory.
Refactors/adjusts example app behavior for Windows constraints: capability gating +
UnsupportedFeatureViewfor unavailable features (notably RAG on Windows), a camera backend abstraction for Vision (to support Windows camera plugin), and small fixes/compat tweaks (dropdownvalueusage, plugin registrant updates, error propagation, and minor theming/logging changes).Reviewed by Cursor Bugbot for commit c29f567. Configure here.