Optimization of lib.lookup function calls#457
Optimization of lib.lookup function calls#457Reef-7 wants to merge 12 commits intoRunanywhereAI:mainfrom
Conversation
| @@ -115,14 +111,9 @@ class DartBridgeVoiceAgent { | |||
|
|
|||
| try { | |||
| final lib = PlatformLoader.loadCommons(); | |||
There was a problem hiding this comment.
Unused lib variable — dead code
lib is assigned but never used in isReady, isSTTLoaded, isLLMLoaded, and isTTSLoaded (lines 113, 131, 149, 167), as well as in getHandle (line 69). The lookup calls were migrated to NativeFunctions.*, but the PlatformLoader.loadCommons() calls were not removed.
This will produce Dart analyzer warnings for unused local variables. These lines should be removed from each affected method.
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart
Line: 113
Comment:
**Unused `lib` variable — dead code**
`lib` is assigned but never used in `isReady`, `isSTTLoaded`, `isLLMLoaded`, and `isTTSLoaded` (lines 113, 131, 149, 167), as well as in `getHandle` (line 69). The lookup calls were migrated to `NativeFunctions.*`, but the `PlatformLoader.loadCommons()` calls were not removed.
This will produce Dart analyzer warnings for unused local variables. These lines should be removed from each affected method.
How can I resolve this? If you propose a fix, please make it concise.| } catch (_) { | ||
| // rac_free may not exist | ||
| } | ||
| NativeFunctions.racFree(audioPtr.value); |
There was a problem hiding this comment.
Removed error handling for missing rac_free symbol
The original code wrapped the rac_free call in a try/catch with the comment // rac_free may not exist. The refactored code calls NativeFunctions.racFree(...) directly. Since NativeFunctions.racFree is a static late final field, if the rac_free symbol is not present in the native library, accessing this field will throw a LateInitializationError (or an ArgumentError from lookupFunction) that is no longer caught here.
If rac_free truly may not exist in some library builds, this change could cause a crash in the finally block during synthesizeSpeech, preventing proper cleanup of audioPtr and audioSizePtr.
Prompt To Fix With AI
This is a comment left during a code review.
Path: sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart
Line: 469
Comment:
**Removed error handling for missing `rac_free` symbol**
The original code wrapped the `rac_free` call in a `try/catch` with the comment `// rac_free may not exist`. The refactored code calls `NativeFunctions.racFree(...)` directly. Since `NativeFunctions.racFree` is a `static late final` field, if the `rac_free` symbol is not present in the native library, accessing this field will throw a `LateInitializationError` (or an `ArgumentError` from `lookupFunction`) that is no longer caught here.
If `rac_free` truly may not exist in some library builds, this change could cause a crash in the `finally` block during `synthesizeSpeech`, preventing proper cleanup of `audioPtr` and `audioSizePtr`.
How can I resolve this? If you propose a fix, please make it concise.|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughReplaces per-call dynamic FFI lookups with a centralized, lazy-cached Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant DartBridgeVoiceAgent
participant NativeFunctions
participant NativeLib
Client->>DartBridgeVoiceAgent: getHandle()
DartBridgeVoiceAgent->>NativeFunctions: voiceAgentCreate(...)
NativeFunctions->>NativeLib: lookup & call rac_voice_agent_create
NativeLib-->>NativeFunctions: handle / int result
NativeFunctions-->>DartBridgeVoiceAgent: result
DartBridgeVoiceAgent-->>Client: Future<RacHandle>
Client->>DartBridgeVoiceAgent: processVoiceTurn(audio)
DartBridgeVoiceAgent->>NativeFunctions: voiceAgentProcessVoiceTurn(handle, audio)
NativeFunctions->>NativeLib: rac_voice_agent_process_voice_turn
NativeLib-->>NativeFunctions: VoiceAgentResult pointer
NativeFunctions-->>DartBridgeVoiceAgent: result pointer
DartBridgeVoiceAgent->>NativeFunctions: voiceAgentResultFree(pointer)
NativeFunctions->>NativeLib: rac_free / voiceAgentResultFree
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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 (1)
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart (1)
68-102:⚠️ Potential issue | 🔴 CriticalCritical: Broken try/catch nesting causes compile error.
There's a dangling
try {at line 80 that breaks the nesting structure. ThegetHandle()method has an outertryat line 68, then anothertry {at line 80 that is never closed properly before thecatchat line 98.🐛 Proposed fix to remove the dangling try
_logger.debug( 'Creating voice agent with shared handles: LLM=$llmHandle, STT=$sttHandle, TTS=$ttsHandle, VAD=$vadHandle'); - try { final handlePtr = calloc<RacVoiceAgentHandle>(); try { final result = NativeFunctions.voiceAgentCreate(🤖 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_voice_agent.dart` around lines 68 - 102, The code has a dangling extra try block beginning before fetching shared handles which breaks nesting; fix by removing the redundant outer try (or closing it properly) so there is a single try around the native create call and its finally/free block. Specifically, keep the sequence that obtains DartBridgeLLM.shared.getHandle(), DartBridgeSTT.shared.getHandle(), DartBridgeTTS.shared.getHandle(), DartBridgeVAD.shared.getHandle() and the _logger.debug call outside (or before) the try, then wrap only the calloc<RacVoiceAgentHandle>() / NativeFunctions.voiceAgentCreate(...) / calloc.free(...) logic in one try/finally with a catch that logs via _logger.error and rethrows; ensure method symbols referenced (voiceAgentCreate, RacVoiceAgentHandle, DartBridgeLLM.shared.getHandle, _handle) remain unchanged.
🧹 Nitpick comments (4)
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_stt.dart (1)
220-243: Consider usingNativeFunctions.sttTranscribefor consistency.The isolate code still performs manual
lookupFunctioncalls. SinceNativeFunctionslazy-initializes per isolate, you could useNativeFunctions.sttTranscribehere for consistency with the PR's goal of centralizing all lookups. The same applies tosttResultFreeat lines 272-274.♻️ Suggested refactor for consistency
- // Get transcribe function - final transcribeFn = lib.lookupFunction< - Int32 Function( - RacHandle, - Pointer<Void>, - IntPtr, - Pointer<RacSttOptionsStruct>, - Pointer<RacSttResultStruct>, - ), - int Function( - RacHandle, - Pointer<Void>, - int, - Pointer<RacSttOptionsStruct>, - Pointer<RacSttResultStruct>, - )>('rac_stt_component_transcribe'); - - final status = transcribeFn( + final status = NativeFunctions.sttTranscribe( handle, dataPtr.cast<Void>(), audioData.length, optionsPtr, resultPtr, );🤖 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_stt.dart` around lines 220 - 243, The code manually looks up rac_stt_component_transcribe into local transcribeFn and calls it; replace that manual lookup and direct call with the centralized lazy lookup NativeFunctions.sttTranscribe and invoke it with the same parameters (handle, dataPtr.cast<Void>(), audioData.length, optionsPtr, resultPtr) to keep lookups per-isolate and consistent; likewise swap the manual free call that uses sttResultFree lookup to use NativeFunctions.sttResultFree instead (referencing transcribeFn/rac_stt_component_transcribe and sttResultFree to locate the existing manual lookups).sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_vad.dart (1)
16-16: Unused import after migration.The
platform_loader.dartimport appears to be unused after migrating all calls toNativeFunctions. Consider removing it.♻️ Remove unused import
import 'package:runanywhere/native/native_functions.dart'; -import 'package:runanywhere/native/platform_loader.dart';🤖 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_vad.dart` at line 16, The import of platform_loader.dart is no longer used in this file; remove the unused import line referencing package:runanywhere/native/platform_loader.dart and ensure all native calls use the NativeFunctions API (e.g., references to NativeFunctions) so there are no remaining references to PlatformLoader; run a quick build or analyzer to confirm no unused-import warnings remain.sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_llm.dart (1)
152-155: Minor: Debug log beforeloadModelwas removed.Per the PR description, the original code had a debug log before calling
loadModel. Only the post-call log remains at line 154. Consider restoring the pre-call log for debugging parity.♻️ Restore pre-call debug log
try { + _logger.debug('Loading LLM model: path=$modelPath, id=$modelId'); final result = NativeFunctions.llmLoadModel(handle, pathPtr, idPtr, namePtr); _logger.debug(🤖 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_llm.dart` around lines 152 - 155, Restore the missing pre-call debug log before invoking NativeFunctions.llmLoadModel by adding an _logger.debug call immediately before the NativeFunctions.llmLoadModel(handle, pathPtr, idPtr, namePtr) invocation that logs the parameters (handle, pathPtr, idPtr, namePtr or their readable representations) to match the existing post-call debug at line 154; locate the call site in dart_bridge_llm.dart (the method that calls NativeFunctions.llmLoadModel) and insert the pre-call _logger.debug with a concise message showing the input values for parity with the existing post-call log.sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart (1)
309-346: Consider usingNativeFunctionsin isolate for consistency.Similar to the STT bridge, the isolate code could use
NativeFunctions.voiceAgentProcessVoiceTurnandNativeFunctions.voiceAgentResultFreefor consistency with the PR's centralization goal, sinceNativeFunctionslazy-initializes per isolate.🤖 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_voice_agent.dart` around lines 309 - 346, The isolate code directly looks up FFI symbols rac_voice_agent_process_voice_turn and rac_voice_agent_result_free instead of reusing the per-isolate lazy-initialized wrappers; replace the direct PlatformLoader.lookup usage in the block around processFn/resultPtr (where processFn is used to call rac_voice_agent_process_voice_turn and freeFn/resultPtr are used to free the result) with calls to NativeFunctions.voiceAgentProcessVoiceTurn and NativeFunctions.voiceAgentResultFree respectively, keeping the same error handling and the call to _parseVoiceTurnResultStatic(resultPtr.ref, lib) and still freeing audioPtr/resultPtr in finally.
🤖 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_voice_agent.dart`:
- Around line 68-102: The code has a dangling extra try block beginning before
fetching shared handles which breaks nesting; fix by removing the redundant
outer try (or closing it properly) so there is a single try around the native
create call and its finally/free block. Specifically, keep the sequence that
obtains DartBridgeLLM.shared.getHandle(), DartBridgeSTT.shared.getHandle(),
DartBridgeTTS.shared.getHandle(), DartBridgeVAD.shared.getHandle() and the
_logger.debug call outside (or before) the try, then wrap only the
calloc<RacVoiceAgentHandle>() / NativeFunctions.voiceAgentCreate(...) /
calloc.free(...) logic in one try/finally with a catch that logs via
_logger.error and rethrows; ensure method symbols referenced (voiceAgentCreate,
RacVoiceAgentHandle, DartBridgeLLM.shared.getHandle, _handle) remain unchanged.
---
Nitpick comments:
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_llm.dart`:
- Around line 152-155: Restore the missing pre-call debug log before invoking
NativeFunctions.llmLoadModel by adding an _logger.debug call immediately before
the NativeFunctions.llmLoadModel(handle, pathPtr, idPtr, namePtr) invocation
that logs the parameters (handle, pathPtr, idPtr, namePtr or their readable
representations) to match the existing post-call debug at line 154; locate the
call site in dart_bridge_llm.dart (the method that calls
NativeFunctions.llmLoadModel) and insert the pre-call _logger.debug with a
concise message showing the input values for parity with the existing post-call
log.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_stt.dart`:
- Around line 220-243: The code manually looks up rac_stt_component_transcribe
into local transcribeFn and calls it; replace that manual lookup and direct call
with the centralized lazy lookup NativeFunctions.sttTranscribe and invoke it
with the same parameters (handle, dataPtr.cast<Void>(), audioData.length,
optionsPtr, resultPtr) to keep lookups per-isolate and consistent; likewise swap
the manual free call that uses sttResultFree lookup to use
NativeFunctions.sttResultFree instead (referencing
transcribeFn/rac_stt_component_transcribe and sttResultFree to locate the
existing manual lookups).
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_vad.dart`:
- Line 16: The import of platform_loader.dart is no longer used in this file;
remove the unused import line referencing
package:runanywhere/native/platform_loader.dart and ensure all native calls use
the NativeFunctions API (e.g., references to NativeFunctions) so there are no
remaining references to PlatformLoader; run a quick build or analyzer to confirm
no unused-import warnings remain.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart`:
- Around line 309-346: The isolate code directly looks up FFI symbols
rac_voice_agent_process_voice_turn and rac_voice_agent_result_free instead of
reusing the per-isolate lazy-initialized wrappers; replace the direct
PlatformLoader.lookup usage in the block around processFn/resultPtr (where
processFn is used to call rac_voice_agent_process_voice_turn and
freeFn/resultPtr are used to free the result) with calls to
NativeFunctions.voiceAgentProcessVoiceTurn and
NativeFunctions.voiceAgentResultFree respectively, keeping the same error
handling and the call to _parseVoiceTurnResultStatic(resultPtr.ref, lib) and
still freeing audioPtr/resultPtr in finally.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 94396ff1-a596-4b74-8182-787dc74aef74
📒 Files selected for processing (6)
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_llm.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/dart_bridge_vad.dartsdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dartsdk/runanywhere-flutter/packages/runanywhere/lib/native/native_functions.dart
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 (1)
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart (1)
63-101:⚠️ Potential issue | 🟠 MajorSerialize
getHandle()initialization to prevent duplicate native instances.Concurrent callers can pass the null-check and both create a native handle; one gets overwritten, leaking native state and breaking singleton assumptions.
Based on learnings: Voice agent implementation must support configuration of individual components (LLM, STT, TTS, VAD) and thread-safe pipeline orchestration.Suggested fix
class DartBridgeVoiceAgent { RacVoiceAgentHandle? _handle; + Future<RacVoiceAgentHandle>? _handleInitFuture; final _logger = SDKLogger('DartBridge.VoiceAgent'); @@ Future<RacVoiceAgentHandle> getHandle() async { if (_handle != null) { return _handle!; } + if (_handleInitFuture != null) { + return _handleInitFuture!; + } - // Use shared component handles (matches Swift approach) - // This allows the voice agent to use already-loaded models from the - // individual component bridges (STT, LLM, TTS, VAD) - final llmHandle = DartBridgeLLM.shared.getHandle(); - final sttHandle = DartBridgeSTT.shared.getHandle(); - final ttsHandle = DartBridgeTTS.shared.getHandle(); - final vadHandle = DartBridgeVAD.shared.getHandle(); + _handleInitFuture = (() async { + final llmHandle = DartBridgeLLM.shared.getHandle(); + final sttHandle = DartBridgeSTT.shared.getHandle(); + final ttsHandle = DartBridgeTTS.shared.getHandle(); + final vadHandle = DartBridgeVAD.shared.getHandle(); - _logger.debug( - 'Creating voice agent with shared handles: LLM=$llmHandle, STT=$sttHandle, TTS=$ttsHandle, VAD=$vadHandle'); + _logger.debug( + 'Creating voice agent with shared handles: LLM=$llmHandle, STT=$sttHandle, TTS=$ttsHandle, VAD=$vadHandle'); - try { - final handlePtr = calloc<RacVoiceAgentHandle>(); try { - final result = NativeFunctions.voiceAgentCreate( - llmHandle, sttHandle, ttsHandle, vadHandle, handlePtr); + final handlePtr = calloc<RacVoiceAgentHandle>(); + try { + final result = NativeFunctions.voiceAgentCreate( + llmHandle, sttHandle, ttsHandle, vadHandle, handlePtr); - if (result != RAC_SUCCESS) { - throw StateError( - 'Failed to create voice agent: ${RacResultCode.getMessage(result)}', - ); - } + if (result != RAC_SUCCESS) { + throw StateError( + 'Failed to create voice agent: ${RacResultCode.getMessage(result)}', + ); + } - _handle = handlePtr.value; - _logger.info('Voice agent created with shared component handles'); - return _handle!; - } finally { - calloc.free(handlePtr); - } - } catch (e) { - _logger.error('Failed to create voice agent handle: $e'); - rethrow; - } + _handle = handlePtr.value; + _logger.info('Voice agent created with shared component handles'); + return _handle!; + } finally { + calloc.free(handlePtr); + } + } catch (e) { + _logger.error('Failed to create voice agent handle: $e'); + rethrow; + } finally { + _handleInitFuture = null; + } + })(); + + return _handleInitFuture!; }🤖 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_voice_agent.dart` around lines 63 - 101, getHandle() can race: multiple callers can see _handle==null and concurrently create native handles, leaking native state; to fix, serialize initialization by adding a private in-flight marker (e.g., Future<RacVoiceAgentHandle>? _initFuture or Completer) and update getHandle to: 1) return _handle if present; 2) if _initFuture exists await it and then return _handle; 3) otherwise create and assign _initFuture before building shared handles (DartBridgeLLM.shared.getHandle, DartBridgeSTT.shared.getHandle, DartBridgeTTS.shared.getHandle, DartBridgeVAD.shared.getHandle), perform native creation, set _handle on success, complete _initFuture, and clear it on error (completeError) so concurrent callers are queued and only one native instance is created; ensure finally blocks still free native pointers and that exceptions rethrow after completing/clearing the in-flight marker.
🧹 Nitpick comments (1)
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart (1)
297-297: Cache isolate-side function pointers for the voice-turn hot path.
processVoiceTurnroutes to a helper that still performsloadCommons + lookupFunctionon every turn, which partially defeats this PR’s lookup optimization in the most performance-sensitive path.Suggested refactor
class DartBridgeVoiceAgent { + static final DynamicLibrary _commonsLib = PlatformLoader.loadCommons(); + static final int Function( + RacVoiceAgentHandle, + Pointer<Void>, + int, + Pointer<RacVoiceAgentResultStruct>) _processVoiceTurnFn = _commonsLib.lookupFunction< + Int32 Function(RacVoiceAgentHandle, Pointer<Void>, IntPtr, + Pointer<RacVoiceAgentResultStruct>), + int Function(RacVoiceAgentHandle, Pointer<Void>, int, + Pointer<RacVoiceAgentResultStruct>)>('rac_voice_agent_process_voice_turn'); + + static final void Function(Pointer<RacVoiceAgentResultStruct>)? _resultFreeFn = (() { + try { + return _commonsLib.lookupFunction< + Void Function(Pointer<RacVoiceAgentResultStruct>), + void Function(Pointer<RacVoiceAgentResultStruct>)>('rac_voice_agent_result_free'); + } catch (_) { + return null; + } + })(); @@ static Future<VoiceTurnResult> _processVoiceTurnInIsolate( @@ - final lib = PlatformLoader.loadCommons(); - final processFn = lib.lookupFunction<...>('rac_voice_agent_process_voice_turn'); - final status = - processFn(handle, audioPtr.cast<Void>(), audioData.length, resultPtr); + final status = + _processVoiceTurnFn(handle, audioPtr.cast<Void>(), audioData.length, resultPtr); @@ - return _parseVoiceTurnResultStatic(resultPtr.ref, lib); + return _parseVoiceTurnResultStatic(resultPtr.ref, _commonsLib); @@ - final lib = PlatformLoader.loadCommons(); - try { - final freeFn = lib.lookupFunction<...>('rac_voice_agent_result_free'); - freeFn(resultPtr); - } catch (e) { - // Function may not exist, just free the struct - } + _resultFreeFn?.call(resultPtr); calloc.free(resultPtr);🤖 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_voice_agent.dart` at line 297, processVoiceTurn currently calls Isolate.run(() => _processVoiceTurnInIsolate(handle, audioData)) but _processVoiceTurnInIsolate performs loadCommons + lookupFunction on every turn; change the isolate-side implementation to cache the results so lookups happen once per isolate. Concretely, introduce isolate-scoped cached variables (e.g., top-level or static late finals) for the result of loadCommons and for the function pointers returned by lookupFunction, and update _processVoiceTurnInIsolate (or replace it with a top-level wrapper invoked by Isolate.run) to use those cached variables instead of calling loadCommons/lookupFunction on each invocation so the hot path reuses the cached function pointers.
🤖 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_voice_agent.dart`:
- Around line 63-101: getHandle() can race: multiple callers can see
_handle==null and concurrently create native handles, leaking native state; to
fix, serialize initialization by adding a private in-flight marker (e.g.,
Future<RacVoiceAgentHandle>? _initFuture or Completer) and update getHandle to:
1) return _handle if present; 2) if _initFuture exists await it and then return
_handle; 3) otherwise create and assign _initFuture before building shared
handles (DartBridgeLLM.shared.getHandle, DartBridgeSTT.shared.getHandle,
DartBridgeTTS.shared.getHandle, DartBridgeVAD.shared.getHandle), perform native
creation, set _handle on success, complete _initFuture, and clear it on error
(completeError) so concurrent callers are queued and only one native instance is
created; ensure finally blocks still free native pointers and that exceptions
rethrow after completing/clearing the in-flight marker.
---
Nitpick comments:
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart`:
- Line 297: processVoiceTurn currently calls Isolate.run(() =>
_processVoiceTurnInIsolate(handle, audioData)) but _processVoiceTurnInIsolate
performs loadCommons + lookupFunction on every turn; change the isolate-side
implementation to cache the results so lookups happen once per isolate.
Concretely, introduce isolate-scoped cached variables (e.g., top-level or static
late finals) for the result of loadCommons and for the function pointers
returned by lookupFunction, and update _processVoiceTurnInIsolate (or replace it
with a top-level wrapper invoked by Isolate.run) to use those cached variables
instead of calling loadCommons/lookupFunction on each invocation so the hot path
reuses the cached function pointers.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 42ca02a6-b2da-404a-827d-879983f605d0
📒 Files selected for processing (1)
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart
|
Hey nice work on this! The centralized lookup pattern is solid. A few things to clean up before merge:
Otherwise LGTM, the core optimization is clean and the isolate methods correctly keep their own |
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 (2)
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart (2)
72-112:⚠️ Potential issue | 🟠 MajorComplete
_initFuturewhen shared-handle creation fails.
_initFutureis published before the four sharedgetHandle()calls, but those calls happen outside the guardedtry. If any one throws, the completer is never completed and every latergetHandle()will wait forever on the stale future.Based on learnings: Voice agent implementation must support configuration of individual components (LLM, STT, TTS, VAD) and thread-safe pipeline orchestration.🐛 Suggested fix
- // Use shared component handles (matches Swift approach) - // This allows the voice agent to use already-loaded models from the - // individual component bridges (STT, LLM, TTS, VAD) - final llmHandle = DartBridgeLLM.shared.getHandle(); - final sttHandle = DartBridgeSTT.shared.getHandle(); - final ttsHandle = DartBridgeTTS.shared.getHandle(); - final vadHandle = DartBridgeVAD.shared.getHandle(); - - _logger.debug( - 'Creating voice agent with shared handles: LLM=$llmHandle, STT=$sttHandle, TTS=$ttsHandle, VAD=$vadHandle'); - try { + // Use shared component handles (matches Swift approach) + // This allows the voice agent to use already-loaded models from the + // individual component bridges (STT, LLM, TTS, VAD) + final llmHandle = DartBridgeLLM.shared.getHandle(); + final sttHandle = DartBridgeSTT.shared.getHandle(); + final ttsHandle = DartBridgeTTS.shared.getHandle(); + final vadHandle = DartBridgeVAD.shared.getHandle(); + + _logger.debug( + 'Creating voice agent with shared handles: LLM=$llmHandle, STT=$sttHandle, TTS=$ttsHandle, VAD=$vadHandle'); + final handlePtr = calloc<RacHandle>(); try { final result = NativeFunctions.voiceAgentCreate( llmHandle, sttHandle, ttsHandle, vadHandle, handlePtr);🤖 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_voice_agent.dart` around lines 72 - 112, The completer assigned to _initFuture is created before the DartBridge*.shared.getHandle() calls and never completed if any getHandle throws; move the creation/assignment of the Completer<RacHandle>/_initFuture to after all four getHandle() calls OR wrap the getHandle() calls in the existing try/catch so that any exception calls completer.completeError(e, st) (and sets _initFuture = null) before rethrowing; reference the symbols _initFuture, Completer<RacHandle>, DartBridgeLLM.shared.getHandle(), DartBridgeSTT.shared.getHandle(), DartBridgeTTS.shared.getHandle(), DartBridgeVAD.shared.getHandle(), completer.completeError to locate and update the logic accordingly.
391-414:⚠️ Potential issue | 🔴 CriticalMemory leak: inner UTF-8 strings from C++ are not freed in
transcribe()andgenerateResponse().These methods call
toDartString()which copies the string contents, butcalloc.free(resultPtr)only releases the outerPointer<Pointer<Utf8>>wrapper. The innerUtf8pointer allocated by C++ is never freed. ThesynthesizeSpeech()method demonstrates the correct pattern—it explicitly callsNativeFunctions.racFree()on pointers returned from native C++ functions before freeing the wrapper.Both methods require the suggested fix to call
NativeFunctions.racFree(resultPtr.value.cast<Void>())before freeingresultPtr.🧹 Suggested fix
Future<String> transcribe(Uint8List audioData) async { @@ } finally { calloc.free(audioPtr); + if (resultPtr.value != nullptr) { + NativeFunctions.racFree(resultPtr.value.cast<Void>()); + } calloc.free(resultPtr); } } @@ Future<String> generateResponse(String prompt) async { @@ } finally { calloc.free(promptPtr); + if (resultPtr.value != nullptr) { + NativeFunctions.racFree(resultPtr.value.cast<Void>()); + } calloc.free(resultPtr); } }🤖 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_voice_agent.dart` around lines 391 - 414, transcribe() (and similarly generateResponse()) currently converts the returned Pointer<Utf8> to a Dart string but only frees the outer Pointer<Pointer<Utf8>>; you must call NativeFunctions.racFree(resultPtr.value.cast<Void>()) to free the inner C++-allocated UTF-8 buffer before freeing resultPtr (follow the pattern used in synthesizeSpeech()); add that racFree call in the try/finally just prior to calloc.free(resultPtr) so both the inner string and the wrapper are released.
🧹 Nitpick comments (2)
sdk/runanywhere-flutter/packages/runanywhere/lib/native/native_functions.dart (1)
403-405: Encoderac_freeoptionality in the registry.The consumer already has to handle builds where
rac_freeis missing. Keeping this binding non-nullable forces every caller to rediscover that ABI edge case via an exception on first access. Please verify the export contract across the native builds you ship; if it is optional, model that once here.♻️ Suggested change
- static late final void Function(Pointer<Void>) racFree = - _lib.lookupFunction<Void Function(Pointer<Void>), - void Function(Pointer<Void>)>('rac_free'); + static late final void Function(Pointer<Void>)? racFree = (() { + try { + return _lib.lookupFunction<Void Function(Pointer<Void>), + void Function(Pointer<Void>)>('rac_free'); + } catch (_) { + return null; + } + })(); + + static void tryRacFree(Pointer<Void> pointer) => racFree?.call(pointer);🤖 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/native_functions.dart` around lines 403 - 405, The binding for racFree is currently non-nullable and will throw on first access if the native export is absent; change the registry entry so racFree is optional (nullable) by using a nullable type for the lookup result (e.g., void Function(Pointer<Void>)? racFree) and assign it only if the native symbol is found via lookup, or use tryLookup to produce null when missing; update callers of racFree to null-check/guard before invoking. Ensure you modify the declaration named racFree and any registry initialization that assigns it so the optionality is encoded at the API boundary.sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart (1)
337-338: Remove the unusedlibplumbing from_parseVoiceTurnResultStatic.The parser no longer uses
lib, so passing_voiceAgentLibthrough this path just leaves an analyzer warning behind.✂️ Suggested cleanup
- return _parseVoiceTurnResultStatic(resultPtr.ref, _voiceAgentLib); + return _parseVoiceTurnResultStatic(resultPtr.ref); @@ - static VoiceTurnResult _parseVoiceTurnResultStatic( - RacVoiceAgentResultStruct result, - DynamicLibrary lib, - ) { + static VoiceTurnResult _parseVoiceTurnResultStatic( + RacVoiceAgentResultStruct result, + ) {Also applies to: 355-358
🤖 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_voice_agent.dart` around lines 337 - 338, The _parseVoiceTurnResultStatic function currently accepts a lib parameter that is unused; remove the lib parameter from the function signature and all call sites that pass _voiceAgentLib (e.g., the call returning _parseVoiceTurnResultStatic(resultPtr.ref, _voiceAgentLib) and the similar call around the later occurrence), update the function declaration and any typedefs to match the new single-argument signature, and run the analyzer to ensure no leftover references to the removed lib parameter remain.
🤖 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_voice_agent.dart`:
- Around line 72-112: The completer assigned to _initFuture is created before
the DartBridge*.shared.getHandle() calls and never completed if any getHandle
throws; move the creation/assignment of the Completer<RacHandle>/_initFuture to
after all four getHandle() calls OR wrap the getHandle() calls in the existing
try/catch so that any exception calls completer.completeError(e, st) (and sets
_initFuture = null) before rethrowing; reference the symbols _initFuture,
Completer<RacHandle>, DartBridgeLLM.shared.getHandle(),
DartBridgeSTT.shared.getHandle(), DartBridgeTTS.shared.getHandle(),
DartBridgeVAD.shared.getHandle(), completer.completeError to locate and update
the logic accordingly.
- Around line 391-414: transcribe() (and similarly generateResponse()) currently
converts the returned Pointer<Utf8> to a Dart string but only frees the outer
Pointer<Pointer<Utf8>>; you must call
NativeFunctions.racFree(resultPtr.value.cast<Void>()) to free the inner
C++-allocated UTF-8 buffer before freeing resultPtr (follow the pattern used in
synthesizeSpeech()); add that racFree call in the try/finally just prior to
calloc.free(resultPtr) so both the inner string and the wrapper are released.
---
Nitpick comments:
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart`:
- Around line 337-338: The _parseVoiceTurnResultStatic function currently
accepts a lib parameter that is unused; remove the lib parameter from the
function signature and all call sites that pass _voiceAgentLib (e.g., the call
returning _parseVoiceTurnResultStatic(resultPtr.ref, _voiceAgentLib) and the
similar call around the later occurrence), update the function declaration and
any typedefs to match the new single-argument signature, and run the analyzer to
ensure no leftover references to the removed lib parameter remain.
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/native_functions.dart`:
- Around line 403-405: The binding for racFree is currently non-nullable and
will throw on first access if the native export is absent; change the registry
entry so racFree is optional (nullable) by using a nullable type for the lookup
result (e.g., void Function(Pointer<Void>)? racFree) and assign it only if the
native symbol is found via lookup, or use tryLookup to produce null when
missing; update callers of racFree to null-check/guard before invoking. Ensure
you modify the declaration named racFree and any registry initialization that
assigns it so the optionality is encoded at the API boundary.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: de2ede2f-8505-42fd-a2c8-4696188a33fc
📒 Files selected for processing (3)
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_llm.dartsdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dartsdk/runanywhere-flutter/packages/runanywhere/lib/native/native_functions.dart
🚧 Files skipped from review as they are similar to previous changes (1)
- sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_llm.dart
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart`:
- Around line 412-414: The unguarded calls to
NativeFunctions.racFree(resultPtr.value.cast<Void>()) in the cleanup/finally
blocks (the calls around the resultPtr usages at the spots matching the earlier
fix) can throw when rac_free is missing; wrap each racFree invocation in a
try-catch like the existing pattern around the rac_free call at the later block
(the one near line 476) so that any missing native symbol or runtime error is
swallowed and does not convert a successful call into a failure—locate the uses
of NativeFunctions.racFree and resultPtr and protect each call with a try {
NativeFunctions.racFree(...) } catch (_) { } block.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 7001cc7c-8231-4fe7-8def-798d9043d3d2
📒 Files selected for processing (1)
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart
Outdated
Show resolved
Hide resolved
|
@coderabbitai review plz |
|
✅ Actions performedReview triggered.
|
sdk/runanywhere-flutter/packages/runanywhere/lib/native/native_functions.dart
Outdated
Show resolved
Hide resolved
sdk/runanywhere-flutter/packages/runanywhere/lib/native/native_functions.dart
Outdated
Show resolved
Hide resolved
|
@coderabbitai review plz |
|
✅ Actions performedReview triggered.
|
|
hey @greptile-apps plz review |
sdk/runanywhere-flutter/packages/runanywhere/lib/native/native_functions.dart
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
🧹 Nitpick comments (2)
sdk/runanywhere-flutter/packages/runanywhere/lib/native/native_functions.dart (2)
360-369: Consider removing unused cached fields.Per the PR discussion,
voiceAgentProcessVoiceTurnandvoiceAgentResultFree(along withllmGenerate,llmGenerateStream,sttTranscribe,ttsSynthesize) are never called because their callers run in background isolates and perform per-isolate inline lookups. These cached main-isolate fields don't provide the intended optimization.Options:
- Remove these unused fields to reduce dead code.
- Alternatively, refactor isolate helpers to use
NativeFunctions.*if isolate-local caching is desired.🤖 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/native_functions.dart` around lines 360 - 369, Remove the unused cached native lookup fields from native_functions.dart—specifically voiceAgentProcessVoiceTurn and voiceAgentResultFree (and likewise llmGenerate, llmGenerateStream, sttTranscribe, ttsSynthesize if present) because callers perform per-isolate inline lookups; delete their static final declarations and associated _lib.lookupFunction calls, remove any now-unused imports or references, and run analyzer/tests to ensure no remaining usages; alternatively, if you prefer isolate-local caching, refactor the isolate helper code to call NativeFunctions.<functionName> (e.g., NativeFunctions.voiceAgentProcessVoiceTurn) instead of doing inline lookups.
408-520: Struct duplication creates maintenance risk.These struct definitions (
RacSttOptionsStruct,RacSttResultStruct,RacTtsOptionsStruct,RacTtsResultStruct) are duplicated fromdart_bridge_stt.dartanddart_bridge_tts.dart. While the comment explains the rationale (avoiding imports), this duplication means changes to C struct layouts must be synchronized in multiple Dart files.Consider moving canonical struct definitions to a shared location (e.g.,
ffi_types.dart) and importing from bothNativeFunctionsand bridge files to establish a single source of truth.🤖 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/native_functions.dart` around lines 408 - 520, Duplicate FFI struct definitions (RacSttOptionsStruct, RacSttResultStruct, RacTtsOptionsStruct, RacTtsResultStruct) should be centralized: move the canonical definitions into a shared file (e.g., ffi_types.dart) and replace the copies in native_functions.dart and dart_bridge_stt.dart/dart_bridge_tts.dart with imports from that shared file; update any references in NativeFunctions and bridge code to use the imported types (e.g., RacSttOptionsStruct, RacSttResultStruct, RacTtsOptionsStruct, RacTtsResultStruct) so there is a single source of truth for the C struct layouts.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In
`@sdk/runanywhere-flutter/packages/runanywhere/lib/native/native_functions.dart`:
- Around line 360-369: Remove the unused cached native lookup fields from
native_functions.dart—specifically voiceAgentProcessVoiceTurn and
voiceAgentResultFree (and likewise llmGenerate, llmGenerateStream,
sttTranscribe, ttsSynthesize if present) because callers perform per-isolate
inline lookups; delete their static final declarations and associated
_lib.lookupFunction calls, remove any now-unused imports or references, and run
analyzer/tests to ensure no remaining usages; alternatively, if you prefer
isolate-local caching, refactor the isolate helper code to call
NativeFunctions.<functionName> (e.g.,
NativeFunctions.voiceAgentProcessVoiceTurn) instead of doing inline lookups.
- Around line 408-520: Duplicate FFI struct definitions (RacSttOptionsStruct,
RacSttResultStruct, RacTtsOptionsStruct, RacTtsResultStruct) should be
centralized: move the canonical definitions into a shared file (e.g.,
ffi_types.dart) and replace the copies in native_functions.dart and
dart_bridge_stt.dart/dart_bridge_tts.dart with imports from that shared file;
update any references in NativeFunctions and bridge code to use the imported
types (e.g., RacSttOptionsStruct, RacSttResultStruct, RacTtsOptionsStruct,
RacTtsResultStruct) so there is a single source of truth for the C struct
layouts.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 22f3d90a-b745-416d-981a-d3faa13ca7a2
📒 Files selected for processing (1)
sdk/runanywhere-flutter/packages/runanywhere/lib/native/native_functions.dart
sdk/runanywhere-flutter/packages/runanywhere/lib/native/native_functions.dart
Outdated
Show resolved
Hide resolved
sdk/runanywhere-flutter/packages/runanywhere/lib/native/native_functions.dart
Outdated
Show resolved
Hide resolved
|
@greptile-apps please review |
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart
Show resolved
Hide resolved
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart
Outdated
Show resolved
Hide resolved
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_vad.dart
Outdated
Show resolved
Hide resolved
|
@greptile-apps you can review |
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_vad.dart
Show resolved
Hide resolved
|
@greptile-apps you can review |
|
Hi @Siddhesh2377 , I’ve implemented the changes and they passed the AI reviews (CodeRabbit/Greptile). |
Sure thing... let me see |
|
Hey @Reef-7 👋 Tried building this locally on an Android device and ran into a couple of compile errors in 1. Missing closing The 2. Missing imports for These are used in the Quick fix would be something like: import 'package:runanywhere/native/dart_bridge_stt.dart' show RacSttResultStruct;
import 'package:runanywhere/native/dart_bridge_vad.dart' show RacVadResultStruct;and adding Everything else in the PR looks solid — the centralized FFI caching, memory leak fixes, and the race condition guard in |
|
@Siddhesh2377 Thank you for the update and informing me with the details, i'll try to work on it as soon as possible and let you know. |
|
@greptile-apps can you review please |
|
Hi @Siddhesh2377, thanks for the feedback.
|
|
Hey @Reef-7 Was the flutter build was compiling for you ? |
|
@Siddhesh2377 Unfortunately, i cannot use the flutter build, since it requires an arm64-v8a ABI Android device from Android Studio(that's what i understood), that i didn't find. |
|
Ahhh I get it now... let me have a look into the build and ping u okay ! |
|
so @Reef-7 U weren't able to Compile even the Flutter SDK ? |
|
@Siddhesh2377 Now i've tested the example app using flutter build apk from the runanywhere-sdks\examples\flutter\RunAnywhereAI folder, the Build was completed without errors as far as i've seen. |
Created native_functions.dart - a single abstract class NativeFunctions with static late final fields for every FFI function across LLM, STT, TTS, VAD, and VoiceAgent. Each field calls lookupFunction exactly once on first access, then caches the result. All five bridge files were updated to call NativeFunctions.* instead of doing inline lookups.
Summary by CodeRabbit
Refactor
Chores
Greptile Summary
This PR centralises all FFI symbol lookups into a new
NativeFunctionsabstract class withstatic finalcached fields, eliminating repeatedlib.lookupFunction/PlatformLoader.loadCommons()calls across the five bridge files. The isolate-bound functions (processVoiceTurn,transcribe,synthesize) are intentionally kept as file-level top-level statics so eachIsolate.run()gets its own initialised copy. The PR also adds a_safeRacFreehelper, aCompleter-based double-init guard ingetHandle(), and fixes pre-existing memory leaks intranscribeAudioandgenerateResponse.Key points:
native_functions.dartimportsdart_bridge_vad.dart(forRacVadResultStruct) whiledart_bridge_vad.dartimportsnative_functions.dart. MovingRacVadResultStructtoffi_types.dartwould break the cycle cleanly.NativeFunctions.sttResultFreeis declared but never called — the STT isolate still uses its own inline lookup, leaving this entry as dead code.Confidence Score: 4/5
libvariables, and the race condition ingetHandle()are resolved. The two remaining items are architectural (circular import) and cosmetic (deadsttResultFreedeclaration), neither of which causes a runtime failure today. The circular import is the only item worth resolving before merge.Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD LLM[dart_bridge_llm.dart] -->|NativeFunctions.*| NF STT[dart_bridge_stt.dart] -->|NativeFunctions.*| NF TTS[dart_bridge_tts.dart] -->|NativeFunctions.*| NF VAD[dart_bridge_vad.dart] -->|NativeFunctions.*| NF VA[dart_bridge_voice_agent.dart] -->|NativeFunctions.*| NF NF[NativeFunctions\nstatic final cached fields] -->|PlatformLoader.loadCommons| LIB[(_lib: DynamicLibrary)] LIB -->|lookupFunction once| SYM[(Native Symbols\nrac_llm_* / rac_stt_*\nrac_tts_* / rac_vad_*\nrac_voice_agent_*)] NF -->|import for RacVadResultStruct ⚠️| VAD VA -->|Isolate.run| ISO[Background Isolate] ISO -->|_processVoiceTurnFn\nfile-level static| IL[(_voiceAgentLib\nper-isolate DynamicLibrary)] IL -->|lookupFunction once per isolate| SYM2[(rac_voice_agent_process_voice_turn\nrac_voice_agent_result_free)]Comments Outside Diff (2)
sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart, line 68-104 (link)Broken try/catch nesting — compile error
The refactor introduced a malformed
tryblock. Line 68 opens atry {but it has no matchingcatchorfinallyclause — this is a syntax error in Dart and will fail to compile.The original code had a single
try/catchwrapping the entire handle creation logic. During the refactoring, thelookupFunctioncall was removed and replaced withNativeFunctions.voiceAgentCreate(...), but a newtry {was inserted at line 82 while the original outertry {at line 68 was left dangling without itscatch.The fix is to remove the extra
tryon line 82 so the originaltry/catchstructure is restored:Prompt To Fix With AI
sdk/runanywhere-flutter/packages/runanywhere/lib/native/native_functions.dart, line 1007 (link)native_functions.dartanddart_bridge_vad.dartnative_functions.dartimportsdart_bridge_vad.dartasvadin order to referencevad.RacVadResultStructin thevadProcesssignature. At the same time,dart_bridge_vad.dartimportsnative_functions.dart. This creates a mutual dependency between the two files.Dart's import resolver handles library cycles without an infinite loop, but circular imports are a recognised code smell and can cause subtle issues in some toolchain configurations (e.g. tree-shaking, static analysis). More concretely, placing a
Structsubclass in the bridge file rather than the shared types file (ffi_types.dart) was always the root cause:RacVadResultStructbelongs alongside the other FFI structs so thatnative_functions.dartcan import it without creating a cycle.The clean fix is to move
RacVadResultStruct(and any other result/options structs that are still in bridge files) toffi_types.dart, then update the import innative_functions.dart:And replace
vad.RacVadResultStructwithRacVadResultStructeverywhere innative_functions.dart.Prompt To Fix With AI
Prompt To Fix All With AI
Reviews (8): Last reviewed commit: "FFI Fix: Resolve type conflicts and fix ..." | Re-trigger Greptile