diff --git a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_llm.dart b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_llm.dart index aff43aa66..40c76bc25 100644 --- a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_llm.dart +++ b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_llm.dart @@ -21,6 +21,7 @@ import 'package:ffi/ffi.dart'; import 'package:runanywhere/foundation/logging/sdk_logger.dart'; import 'package:runanywhere/native/ffi_types.dart'; +import 'package:runanywhere/native/native_functions.dart'; import 'package:runanywhere/native/platform_loader.dart'; /// LLM component bridge for C++ interop. @@ -78,13 +79,9 @@ class DartBridgeLLM { } try { - final lib = PlatformLoader.loadCommons(); - final create = lib.lookupFunction), - int Function(Pointer)>('rac_llm_component_create'); - final handlePtr = calloc(); try { - final result = create(handlePtr); + final result = NativeFunctions.llmCreate(handlePtr); if (result != RAC_SUCCESS) { throw StateError( @@ -111,11 +108,7 @@ class DartBridgeLLM { if (_handle == null) return false; try { - final lib = PlatformLoader.loadCommons(); - final isLoadedFn = lib.lookupFunction('rac_llm_component_is_loaded'); - - return isLoadedFn(_handle!) == RAC_TRUE; + return NativeFunctions.llmIsLoaded(_handle!) == RAC_TRUE; } catch (e) { _logger.debug('isLoaded check failed: $e'); return false; @@ -130,11 +123,7 @@ class DartBridgeLLM { if (_handle == null) return false; try { - final lib = PlatformLoader.loadCommons(); - final supportsStreamingFn = lib.lookupFunction('rac_llm_component_supports_streaming'); - - return supportsStreamingFn(_handle!) == RAC_TRUE; + return NativeFunctions.llmSupportsStreaming(_handle!) == RAC_TRUE; } catch (e) { return false; } @@ -161,16 +150,8 @@ class DartBridgeLLM { final namePtr = modelName.toNativeUtf8(); try { - final lib = PlatformLoader.loadCommons(); - final loadModelFn = lib.lookupFunction< - Int32 Function( - RacHandle, Pointer, Pointer, Pointer), - int Function(RacHandle, Pointer, Pointer, - Pointer)>('rac_llm_component_load_model'); - - _logger.debug( - 'Calling rac_llm_component_load_model with handle: $_handle, path: $modelPath'); - final result = loadModelFn(handle, pathPtr, idPtr, namePtr); + _logger.debug('Calling rac_llm_component_load_model with handle=$handle'); + final result = NativeFunctions.llmLoadModel(handle, pathPtr, idPtr, namePtr); _logger.debug( 'rac_llm_component_load_model returned: $result (${RacResultCode.getMessage(result)})'); @@ -194,11 +175,7 @@ class DartBridgeLLM { if (_handle == null) return; try { - final lib = PlatformLoader.loadCommons(); - final cleanupFn = lib.lookupFunction('rac_llm_component_cleanup'); - - cleanupFn(_handle!); + NativeFunctions.llmCleanup(_handle!); _loadedModelId = null; _logger.info('LLM model unloaded'); } catch (e) { @@ -211,11 +188,7 @@ class DartBridgeLLM { if (_handle == null) return; try { - final lib = PlatformLoader.loadCommons(); - final cancelFn = lib.lookupFunction('rac_llm_component_cancel'); - - cancelFn(_handle!); + NativeFunctions.llmCancel(_handle!); _logger.debug('LLM generation cancelled'); } catch (e) { _logger.error('Failed to cancel generation: $e'); @@ -373,11 +346,7 @@ class DartBridgeLLM { void destroy() { if (_handle != null) { try { - final lib = PlatformLoader.loadCommons(); - final destroyFn = lib.lookupFunction('rac_llm_component_destroy'); - - destroyFn(_handle!); + NativeFunctions.llmDestroy(_handle!); _handle = null; _loadedModelId = null; _logger.debug('LLM component destroyed'); @@ -486,7 +455,7 @@ void _streamingIsolateEntry(_StreamingIsolateParams params) { // Set systemPrompt if provided if (params.systemPrompt != null && params.systemPrompt!.isNotEmpty) { systemPromptPtr = params.systemPrompt!.toNativeUtf8(); - optionsPtr.ref.systemPrompt = systemPromptPtr!; + optionsPtr.ref.systemPrompt = systemPromptPtr; } else { optionsPtr.ref.systemPrompt = nullptr; } @@ -553,7 +522,7 @@ void _streamingIsolateEntry(_StreamingIsolateParams params) { calloc.free(promptPtr); calloc.free(optionsPtr); if (systemPromptPtr != null) { - calloc.free(systemPromptPtr!); + calloc.free(systemPromptPtr); } _isolateSendPort = null; } @@ -622,7 +591,7 @@ _IsolateGenerationResult _generateInIsolate( // Set systemPrompt if provided if (systemPrompt != null && systemPrompt.isNotEmpty) { systemPromptPtr = systemPrompt.toNativeUtf8(); - optionsPtr.ref.systemPrompt = systemPromptPtr!; + optionsPtr.ref.systemPrompt = systemPromptPtr; } else { optionsPtr.ref.systemPrompt = nullptr; } @@ -658,7 +627,7 @@ _IsolateGenerationResult _generateInIsolate( calloc.free(optionsPtr); calloc.free(resultPtr); if (systemPromptPtr != null) { - calloc.free(systemPromptPtr!); + calloc.free(systemPromptPtr); } } } diff --git a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_stt.dart b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_stt.dart index e1be97a2d..eb428ad00 100644 --- a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_stt.dart +++ b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_stt.dart @@ -12,6 +12,7 @@ import 'package:ffi/ffi.dart'; import 'package:runanywhere/foundation/logging/sdk_logger.dart'; import 'package:runanywhere/native/ffi_types.dart'; +import 'package:runanywhere/native/native_functions.dart'; import 'package:runanywhere/native/platform_loader.dart'; /// STT component bridge for C++ interop. @@ -48,13 +49,9 @@ class DartBridgeSTT { } try { - final lib = PlatformLoader.loadCommons(); - final create = lib.lookupFunction), - int Function(Pointer)>('rac_stt_component_create'); - final handlePtr = calloc(); try { - final result = create(handlePtr); + final result = NativeFunctions.sttCreate(handlePtr); if (result != RAC_SUCCESS) { throw StateError( @@ -81,11 +78,7 @@ class DartBridgeSTT { if (_handle == null) return false; try { - final lib = PlatformLoader.loadCommons(); - final isLoadedFn = lib.lookupFunction('rac_stt_component_is_loaded'); - - return isLoadedFn(_handle!) == RAC_TRUE; + return NativeFunctions.sttIsLoaded(_handle!) == RAC_TRUE; } catch (e) { _logger.debug('isLoaded check failed: $e'); return false; @@ -100,11 +93,7 @@ class DartBridgeSTT { if (_handle == null) return false; try { - final lib = PlatformLoader.loadCommons(); - final supportsStreamingFn = lib.lookupFunction('rac_stt_component_supports_streaming'); - - return supportsStreamingFn(_handle!) == RAC_TRUE; + return NativeFunctions.sttSupportsStreaming(_handle!) == RAC_TRUE; } catch (e) { return false; } @@ -131,14 +120,7 @@ class DartBridgeSTT { final namePtr = modelName.toNativeUtf8(); try { - final lib = PlatformLoader.loadCommons(); - final loadModelFn = lib.lookupFunction< - Int32 Function( - RacHandle, Pointer, Pointer, Pointer), - int Function(RacHandle, Pointer, Pointer, - Pointer)>('rac_stt_component_load_model'); - - final result = loadModelFn(handle, pathPtr, idPtr, namePtr); + final result = NativeFunctions.sttLoadModel(handle, pathPtr, idPtr, namePtr); if (result != RAC_SUCCESS) { throw StateError( @@ -160,11 +142,7 @@ class DartBridgeSTT { if (_handle == null) return; try { - final lib = PlatformLoader.loadCommons(); - final cleanupFn = lib.lookupFunction('rac_stt_component_cleanup'); - - cleanupFn(_handle!); + NativeFunctions.sttCleanup(_handle!); _loadedModelId = null; _logger.info('STT model unloaded'); } catch (e) { @@ -357,11 +335,7 @@ class DartBridgeSTT { void destroy() { if (_handle != null) { try { - final lib = PlatformLoader.loadCommons(); - final destroyFn = lib.lookupFunction('rac_stt_component_destroy'); - - destroyFn(_handle!); + NativeFunctions.sttDestroy(_handle!); _handle = null; _loadedModelId = null; _logger.debug('STT component destroyed'); diff --git a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_tts.dart b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_tts.dart index 68dea8c3f..4c82afcb3 100644 --- a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_tts.dart +++ b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_tts.dart @@ -12,6 +12,7 @@ import 'package:ffi/ffi.dart'; import 'package:runanywhere/foundation/logging/sdk_logger.dart'; import 'package:runanywhere/native/ffi_types.dart'; +import 'package:runanywhere/native/native_functions.dart'; import 'package:runanywhere/native/platform_loader.dart'; /// TTS component bridge for C++ interop. @@ -48,13 +49,9 @@ class DartBridgeTTS { } try { - final lib = PlatformLoader.loadCommons(); - final create = lib.lookupFunction), - int Function(Pointer)>('rac_tts_component_create'); - final handlePtr = calloc(); try { - final result = create(handlePtr); + final result = NativeFunctions.ttsCreate(handlePtr); if (result != RAC_SUCCESS) { throw StateError( @@ -81,11 +78,7 @@ class DartBridgeTTS { if (_handle == null) return false; try { - final lib = PlatformLoader.loadCommons(); - final isLoadedFn = lib.lookupFunction('rac_tts_component_is_loaded'); - - return isLoadedFn(_handle!) == RAC_TRUE; + return NativeFunctions.ttsIsLoaded(_handle!) == RAC_TRUE; } catch (e) { _logger.debug('isLoaded check failed: $e'); return false; @@ -116,14 +109,7 @@ class DartBridgeTTS { final namePtr = voiceName.toNativeUtf8(); try { - final lib = PlatformLoader.loadCommons(); - final loadVoiceFn = lib.lookupFunction< - Int32 Function( - RacHandle, Pointer, Pointer, Pointer), - int Function(RacHandle, Pointer, Pointer, - Pointer)>('rac_tts_component_load_voice'); - - final result = loadVoiceFn(handle, pathPtr, idPtr, namePtr); + final result = NativeFunctions.ttsLoadVoice(handle, pathPtr, idPtr, namePtr); if (result != RAC_SUCCESS) { throw StateError( @@ -145,11 +131,7 @@ class DartBridgeTTS { if (_handle == null) return; try { - final lib = PlatformLoader.loadCommons(); - final cleanupFn = lib.lookupFunction('rac_tts_component_cleanup'); - - cleanupFn(_handle!); + NativeFunctions.ttsCleanup(_handle!); _loadedVoiceId = null; _logger.info('TTS voice unloaded'); } catch (e) { @@ -162,11 +144,7 @@ class DartBridgeTTS { if (_handle == null) return; try { - final lib = PlatformLoader.loadCommons(); - final stopFn = lib.lookupFunction('rac_tts_component_stop'); - - stopFn(_handle!); + NativeFunctions.ttsStop(_handle!); _logger.debug('TTS synthesis stopped'); } catch (e) { _logger.error('Failed to stop TTS: $e'); @@ -335,11 +313,7 @@ class DartBridgeTTS { void destroy() { if (_handle != null) { try { - final lib = PlatformLoader.loadCommons(); - final destroyFn = lib.lookupFunction('rac_tts_component_destroy'); - - destroyFn(_handle!); + NativeFunctions.ttsDestroy(_handle!); _handle = null; _loadedVoiceId = null; _logger.debug('TTS component destroyed'); diff --git a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_vad.dart b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_vad.dart index 8937f6c18..22500872c 100644 --- a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_vad.dart +++ b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_vad.dart @@ -12,7 +12,7 @@ import 'package:ffi/ffi.dart'; import 'package:runanywhere/foundation/logging/sdk_logger.dart'; import 'package:runanywhere/native/ffi_types.dart'; -import 'package:runanywhere/native/platform_loader.dart'; +import 'package:runanywhere/native/native_functions.dart'; /// VAD component bridge for C++ interop. /// @@ -54,14 +54,9 @@ class DartBridgeVAD { } try { - final lib = PlatformLoader.loadCommons(); - final create = lib.lookupFunction< - Int32 Function(Pointer), - int Function(Pointer)>('rac_vad_component_create'); - final handlePtr = calloc(); try { - final result = create(handlePtr); + final result = NativeFunctions.vadCreate(handlePtr); if (result != RAC_SUCCESS) { throw StateError( @@ -88,11 +83,7 @@ class DartBridgeVAD { if (_handle == null) return false; try { - final lib = PlatformLoader.loadCommons(); - final isInitializedFn = lib.lookupFunction('rac_vad_component_is_initialized'); - - return isInitializedFn(_handle!) == RAC_TRUE; + return NativeFunctions.vadIsInitialized(_handle!) == RAC_TRUE; } catch (e) { _logger.debug('isInitialized check failed: $e'); return false; @@ -104,11 +95,7 @@ class DartBridgeVAD { if (_handle == null) return false; try { - final lib = PlatformLoader.loadCommons(); - final isSpeechActiveFn = lib.lookupFunction('rac_vad_component_is_speech_active'); - - return isSpeechActiveFn(_handle!) == RAC_TRUE; + return NativeFunctions.vadIsSpeechActive(_handle!) == RAC_TRUE; } catch (e) { return false; } @@ -119,11 +106,7 @@ class DartBridgeVAD { if (_handle == null) return 0.0; try { - final lib = PlatformLoader.loadCommons(); - final getThresholdFn = lib.lookupFunction('rac_vad_component_get_energy_threshold'); - - return getThresholdFn(_handle!); + return NativeFunctions.vadGetEnergyThreshold(_handle!); } catch (e) { return 0.0; } @@ -134,13 +117,7 @@ class DartBridgeVAD { if (_handle == null) return; try { - final lib = PlatformLoader.loadCommons(); - final setThresholdFn = lib.lookupFunction< - Int32 Function(RacHandle, Float), - int Function( - RacHandle, double)>('rac_vad_component_set_energy_threshold'); - - setThresholdFn(_handle!, threshold); + NativeFunctions.vadSetEnergyThreshold(_handle!, threshold); } catch (e) { _logger.error('Failed to set energy threshold: $e'); } @@ -155,11 +132,7 @@ class DartBridgeVAD { final handle = getHandle(); try { - final lib = PlatformLoader.loadCommons(); - final initializeFn = lib.lookupFunction('rac_vad_component_initialize'); - - final result = initializeFn(handle); + final result = NativeFunctions.vadInitialize(handle); if (result != RAC_SUCCESS) { throw StateError( @@ -179,11 +152,7 @@ class DartBridgeVAD { if (_handle == null) return; try { - final lib = PlatformLoader.loadCommons(); - final startFn = lib.lookupFunction('rac_vad_component_start'); - - final result = startFn(_handle!); + final result = NativeFunctions.vadStart(_handle!); if (result != RAC_SUCCESS) { throw StateError( 'Failed to start VAD: ${RacResultCode.getMessage(result)}', @@ -201,11 +170,7 @@ class DartBridgeVAD { if (_handle == null) return; try { - final lib = PlatformLoader.loadCommons(); - final stopFn = lib.lookupFunction('rac_vad_component_stop'); - - stopFn(_handle!); + NativeFunctions.vadStop(_handle!); _logger.debug('VAD stopped'); } catch (e) { _logger.error('Failed to stop VAD: $e'); @@ -217,11 +182,7 @@ class DartBridgeVAD { if (_handle == null) return; try { - final lib = PlatformLoader.loadCommons(); - final resetFn = lib.lookupFunction('rac_vad_component_reset'); - - resetFn(_handle!); + NativeFunctions.vadReset(_handle!); _logger.debug('VAD reset'); } catch (e) { _logger.error('Failed to reset VAD: $e'); @@ -233,11 +194,7 @@ class DartBridgeVAD { if (_handle == null) return; try { - final lib = PlatformLoader.loadCommons(); - final cleanupFn = lib.lookupFunction('rac_vad_component_cleanup'); - - cleanupFn(_handle!); + NativeFunctions.vadCleanup(_handle!); _logger.info('VAD cleaned up'); } catch (e) { _logger.error('Failed to cleanup VAD: $e'); @@ -268,14 +225,12 @@ class DartBridgeVAD { samplesPtr[i] = samples[i]; } - final lib = PlatformLoader.loadCommons(); - final processFn = lib.lookupFunction< - Int32 Function( - RacHandle, Pointer, IntPtr, Pointer), - int Function(RacHandle, Pointer, int, - Pointer)>('rac_vad_component_process'); - - final status = processFn(handle, samplesPtr, samples.length, resultPtr); + final status = NativeFunctions.vadProcess( + handle, + samplesPtr, + samples.length, + resultPtr, + ); if (status != RAC_SUCCESS) { throw StateError( @@ -315,11 +270,7 @@ class DartBridgeVAD { void destroy() { if (_handle != null) { try { - final lib = PlatformLoader.loadCommons(); - final destroyFn = lib.lookupFunction('rac_vad_component_destroy'); - - destroyFn(_handle!); + NativeFunctions.vadDestroy(_handle!); _handle = null; _logger.debug('VAD component destroyed'); } catch (e) { diff --git a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart index 2dd613f43..2f2e36381 100644 --- a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart +++ b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/dart_bridge_voice_agent.dart @@ -17,10 +17,18 @@ import 'package:runanywhere/native/dart_bridge_stt.dart'; import 'package:runanywhere/native/dart_bridge_tts.dart'; import 'package:runanywhere/native/dart_bridge_vad.dart'; import 'package:runanywhere/native/ffi_types.dart'; +import 'package:runanywhere/native/native_functions.dart'; import 'package:runanywhere/native/platform_loader.dart'; -/// Voice agent handle type (opaque pointer to rac_voice_agent struct). -typedef RacVoiceAgentHandle = Pointer; +void _safeRacFree(Pointer ptr) { + if (ptr == nullptr) return; + + try { + NativeFunctions.racFree?.call(ptr); + } catch (_) { + // rac_free may not exist in some native builds + } +} /// VoiceAgent component bridge for C++ interop. /// @@ -44,7 +52,8 @@ class DartBridgeVoiceAgent { // MARK: - State - RacVoiceAgentHandle? _handle; + RacHandle? _handle; + Future? _initFuture; final _logger = SDKLogger('DartBridge.VoiceAgent'); /// Event stream controller @@ -59,14 +68,19 @@ class DartBridgeVoiceAgent { /// /// Requires LLM, STT, TTS, and VAD components to be available. /// Uses shared component handles (matches Swift CppBridge+VoiceAgent.swift). - Future getHandle() async { + Future getHandle() async { if (_handle != null) { return _handle!; } - try { - final lib = PlatformLoader.loadCommons(); + if (_initFuture != null) { + return _initFuture!; + } + final completer = Completer(); + _initFuture = completer.future; + + 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) @@ -78,16 +92,10 @@ class DartBridgeVoiceAgent { _logger.debug( 'Creating voice agent with shared handles: LLM=$llmHandle, STT=$sttHandle, TTS=$ttsHandle, VAD=$vadHandle'); - final create = lib.lookupFunction< - Int32 Function(RacHandle, RacHandle, RacHandle, RacHandle, - Pointer), - int Function(RacHandle, RacHandle, RacHandle, RacHandle, - Pointer)>('rac_voice_agent_create'); - - final handlePtr = calloc(); + final handlePtr = calloc(); try { - final result = - create(llmHandle, sttHandle, ttsHandle, vadHandle, handlePtr); + final result = NativeFunctions.voiceAgentCreate( + llmHandle, sttHandle, ttsHandle, vadHandle, handlePtr); if (result != RAC_SUCCESS) { throw StateError( @@ -97,12 +105,18 @@ class DartBridgeVoiceAgent { _handle = handlePtr.value; _logger.info('Voice agent created with shared component handles'); + completer.complete(_handle!); + _initFuture = null; return _handle!; } finally { calloc.free(handlePtr); } - } catch (e) { + } catch (e, st) { _logger.error('Failed to create voice agent handle: $e'); + if (!completer.isCompleted) { + completer.completeError(e, st); + } + _initFuture = null; rethrow; } } @@ -114,15 +128,9 @@ class DartBridgeVoiceAgent { if (_handle == null) return false; try { - final lib = PlatformLoader.loadCommons(); - final isReadyFn = lib.lookupFunction< - Int32 Function(RacVoiceAgentHandle, Pointer), - int Function( - RacVoiceAgentHandle, Pointer)>('rac_voice_agent_is_ready'); - final readyPtr = calloc(); try { - final result = isReadyFn(_handle!, readyPtr); + final result = NativeFunctions.voiceAgentIsReady(_handle!, readyPtr); return result == RAC_SUCCESS && readyPtr.value == RAC_TRUE; } finally { calloc.free(readyPtr); @@ -137,15 +145,10 @@ class DartBridgeVoiceAgent { if (_handle == null) return false; try { - final lib = PlatformLoader.loadCommons(); - final isLoadedFn = lib.lookupFunction< - Int32 Function(RacVoiceAgentHandle, Pointer), - int Function(RacVoiceAgentHandle, - Pointer)>('rac_voice_agent_is_stt_loaded'); - final loadedPtr = calloc(); try { - final result = isLoadedFn(_handle!, loadedPtr); + final result = + NativeFunctions.voiceAgentIsSTTLoaded(_handle!, loadedPtr); return result == RAC_SUCCESS && loadedPtr.value == RAC_TRUE; } finally { calloc.free(loadedPtr); @@ -160,15 +163,10 @@ class DartBridgeVoiceAgent { if (_handle == null) return false; try { - final lib = PlatformLoader.loadCommons(); - final isLoadedFn = lib.lookupFunction< - Int32 Function(RacVoiceAgentHandle, Pointer), - int Function(RacVoiceAgentHandle, - Pointer)>('rac_voice_agent_is_llm_loaded'); - final loadedPtr = calloc(); try { - final result = isLoadedFn(_handle!, loadedPtr); + final result = + NativeFunctions.voiceAgentIsLLMLoaded(_handle!, loadedPtr); return result == RAC_SUCCESS && loadedPtr.value == RAC_TRUE; } finally { calloc.free(loadedPtr); @@ -183,15 +181,10 @@ class DartBridgeVoiceAgent { if (_handle == null) return false; try { - final lib = PlatformLoader.loadCommons(); - final isLoadedFn = lib.lookupFunction< - Int32 Function(RacVoiceAgentHandle, Pointer), - int Function(RacVoiceAgentHandle, - Pointer)>('rac_voice_agent_is_tts_loaded'); - final loadedPtr = calloc(); try { - final result = isLoadedFn(_handle!, loadedPtr); + final result = + NativeFunctions.voiceAgentIsTTSLoaded(_handle!, loadedPtr); return result == RAC_SUCCESS && loadedPtr.value == RAC_TRUE; } finally { calloc.free(loadedPtr); @@ -211,13 +204,8 @@ class DartBridgeVoiceAgent { final idPtr = modelId.toNativeUtf8(); try { - final lib = PlatformLoader.loadCommons(); - final loadFn = lib.lookupFunction< - Int32 Function(RacVoiceAgentHandle, Pointer, Pointer), - int Function(RacVoiceAgentHandle, Pointer, - Pointer)>('rac_voice_agent_load_stt_model'); - - final result = loadFn(handle, pathPtr, idPtr); + final result = + NativeFunctions.voiceAgentLoadSTTModel(handle, pathPtr, idPtr); if (result != RAC_SUCCESS) { throw StateError( @@ -241,13 +229,8 @@ class DartBridgeVoiceAgent { final idPtr = modelId.toNativeUtf8(); try { - final lib = PlatformLoader.loadCommons(); - final loadFn = lib.lookupFunction< - Int32 Function(RacVoiceAgentHandle, Pointer, Pointer), - int Function(RacVoiceAgentHandle, Pointer, - Pointer)>('rac_voice_agent_load_llm_model'); - - final result = loadFn(handle, pathPtr, idPtr); + final result = + NativeFunctions.voiceAgentLoadLLMModel(handle, pathPtr, idPtr); if (result != RAC_SUCCESS) { throw StateError( @@ -271,13 +254,8 @@ class DartBridgeVoiceAgent { final idPtr = voiceId.toNativeUtf8(); try { - final lib = PlatformLoader.loadCommons(); - final loadFn = lib.lookupFunction< - Int32 Function(RacVoiceAgentHandle, Pointer, Pointer), - int Function(RacVoiceAgentHandle, Pointer, - Pointer)>('rac_voice_agent_load_tts_voice'); - - final result = loadFn(handle, pathPtr, idPtr); + final result = + NativeFunctions.voiceAgentLoadTTSVoice(handle, pathPtr, idPtr); if (result != RAC_SUCCESS) { throw StateError( @@ -302,12 +280,8 @@ class DartBridgeVoiceAgent { final handle = await getHandle(); try { - final lib = PlatformLoader.loadCommons(); - final initFn = lib.lookupFunction( - 'rac_voice_agent_initialize_with_loaded_models'); - - final result = initFn(handle); + final result = + NativeFunctions.voiceAgentInitializeWithLoadedModels(handle); if (result != RAC_SUCCESS) { throw StateError( @@ -341,14 +315,13 @@ class DartBridgeVoiceAgent { } // Run the heavy C++ processing in a background isolate - return Isolate.run( - () => _processVoiceTurnInIsolate(handle, audioData)); + return Isolate.run(() => _processVoiceTurnInIsolate(handle, audioData)); } /// Static helper for processing voice turn in an isolate. /// The C++ API expects raw audio bytes (PCM16), not float samples. static Future _processVoiceTurnInIsolate( - RacVoiceAgentHandle handle, + RacHandle handle, Uint8List audioData, ) async { // Allocate native memory for audio data (raw PCM16 bytes) @@ -359,16 +332,8 @@ class DartBridgeVoiceAgent { // Efficient bulk copy of audio bytes audioPtr.asTypedList(audioData.length).setAll(0, audioData); - final lib = PlatformLoader.loadCommons(); - final processFn = lib.lookupFunction< - Int32 Function(RacVoiceAgentHandle, Pointer, IntPtr, - Pointer), - int Function(RacVoiceAgentHandle, Pointer, int, - Pointer)>( - 'rac_voice_agent_process_voice_turn'); - - final status = - processFn(handle, audioPtr.cast(), audioData.length, resultPtr); + final status = _processVoiceTurnFn( + handle, audioPtr.cast(), audioData.length, resultPtr); if (status != RAC_SUCCESS) { throw StateError( @@ -377,20 +342,14 @@ class DartBridgeVoiceAgent { } // Parse result while still in isolate (before freeing memory) - return _parseVoiceTurnResultStatic(resultPtr.ref, lib); + return _parseVoiceTurnResultStatic(resultPtr.ref); } finally { // Free audio data calloc.free(audioPtr); // Free result struct - the C++ side allocates strings/audio that need freeing - final lib = PlatformLoader.loadCommons(); try { - final freeFn = lib.lookupFunction< - Void Function(Pointer), - void Function(Pointer)>( - 'rac_voice_agent_result_free', - ); - freeFn(resultPtr); + _voiceAgentResultFreeFn?.call(resultPtr); } catch (e) { // Function may not exist, just free the struct } @@ -403,7 +362,6 @@ class DartBridgeVoiceAgent { /// using rac_audio_float32_to_wav, so synthesized_audio is WAV data. static VoiceTurnResult _parseVoiceTurnResultStatic( RacVoiceAgentResultStruct result, - DynamicLibrary lib, ) { final transcription = result.transcription != nullptr ? result.transcription.toDartString() @@ -416,7 +374,9 @@ class DartBridgeVoiceAgent { Uint8List audioWavData; if (result.synthesizedAudioSize > 0 && result.synthesizedAudio != nullptr) { audioWavData = Uint8List.fromList( - result.synthesizedAudio.cast().asTypedList(result.synthesizedAudioSize), + result.synthesizedAudio + .cast() + .asTypedList(result.synthesizedAudioSize), ); } else { audioWavData = Uint8List(0); @@ -446,14 +406,7 @@ class DartBridgeVoiceAgent { // Efficient bulk copy of audio bytes audioPtr.asTypedList(audioData.length).setAll(0, audioData); - final lib = PlatformLoader.loadCommons(); - final transcribeFn = lib.lookupFunction< - Int32 Function(RacVoiceAgentHandle, Pointer, IntPtr, - Pointer>), - int Function(RacVoiceAgentHandle, Pointer, int, - Pointer>)>('rac_voice_agent_transcribe'); - - final status = transcribeFn( + final status = NativeFunctions.voiceAgentTranscribe( handle, audioPtr.cast(), audioData.length, resultPtr); if (status != RAC_SUCCESS) { @@ -464,6 +417,7 @@ class DartBridgeVoiceAgent { return resultPtr.value != nullptr ? resultPtr.value.toDartString() : ''; } finally { calloc.free(audioPtr); + _safeRacFree(resultPtr.value.cast()); calloc.free(resultPtr); } } @@ -476,14 +430,8 @@ class DartBridgeVoiceAgent { final resultPtr = calloc>(); try { - final lib = PlatformLoader.loadCommons(); - final generateFn = lib.lookupFunction< - Int32 Function( - RacVoiceAgentHandle, Pointer, Pointer>), - int Function(RacVoiceAgentHandle, Pointer, - Pointer>)>('rac_voice_agent_generate_response'); - - final status = generateFn(handle, promptPtr, resultPtr); + final status = NativeFunctions.voiceAgentGenerateResponse( + handle, promptPtr, resultPtr); if (status != RAC_SUCCESS) { throw StateError( @@ -493,6 +441,7 @@ class DartBridgeVoiceAgent { return resultPtr.value != nullptr ? resultPtr.value.toDartString() : ''; } finally { calloc.free(promptPtr); + _safeRacFree(resultPtr.value.cast()); calloc.free(resultPtr); } } @@ -507,17 +456,8 @@ class DartBridgeVoiceAgent { final audioSizePtr = calloc(); try { - final lib = PlatformLoader.loadCommons(); - final synthesizeFn = lib.lookupFunction< - Int32 Function(RacVoiceAgentHandle, Pointer, - Pointer>, Pointer), - int Function( - RacVoiceAgentHandle, - Pointer, - Pointer>, - Pointer)>('rac_voice_agent_synthesize_speech'); - - final status = synthesizeFn(handle, textPtr, audioPtr, audioSizePtr); + final status = NativeFunctions.voiceAgentSynthesizeSpeech( + handle, textPtr, audioPtr, audioSizePtr); if (status != RAC_SUCCESS) { throw StateError( @@ -535,16 +475,7 @@ class DartBridgeVoiceAgent { } finally { calloc.free(textPtr); // Free the audio data allocated by C++ - if (audioPtr.value != nullptr) { - final lib = PlatformLoader.loadCommons(); - try { - final freeFn = lib.lookupFunction), - void Function(Pointer)>('rac_free'); - freeFn(audioPtr.value); - } catch (_) { - // rac_free may not exist - } - } + _safeRacFree(audioPtr.value); calloc.free(audioPtr); calloc.free(audioSizePtr); } @@ -557,11 +488,7 @@ class DartBridgeVoiceAgent { if (_handle == null) return; try { - final lib = PlatformLoader.loadCommons(); - final cleanupFn = lib.lookupFunction('rac_voice_agent_cleanup'); - - cleanupFn(_handle!); + NativeFunctions.voiceAgentCleanup(_handle!); _logger.info('Voice agent cleaned up'); } catch (e) { _logger.error('Failed to cleanup voice agent: $e'); @@ -572,11 +499,7 @@ class DartBridgeVoiceAgent { void destroy() { if (_handle != null) { try { - final lib = PlatformLoader.loadCommons(); - final destroyFn = lib.lookupFunction('rac_voice_agent_destroy'); - - destroyFn(_handle!); + NativeFunctions.voiceAgentDestroy(_handle!); _handle = null; _logger.debug('Voice agent destroyed'); } catch (e) { @@ -601,6 +524,7 @@ class DartBridgeVoiceAgent { class VoiceTurnResult { final String transcription; final String response; + /// WAV-formatted audio data ready for playback final Uint8List audioWavData; final int sttDurationMs; @@ -678,3 +602,35 @@ final class RacVoiceAgentResultStruct extends Struct { @IntPtr() external int synthesizedAudioSize; // size_t (size in bytes) } + +// MARK: - Isolate-scoped FFI caches + +// These are intentionally top-level statics so each isolate initializes them +// once on first use. This keeps symbol lookups out of hot paths while preserving +// the existing isolate execution model. +final DynamicLibrary _voiceAgentLib = PlatformLoader.loadCommons(); + +final int Function( + RacHandle, + Pointer, + int, + Pointer, +) _processVoiceTurnFn = _voiceAgentLib.lookupFunction< + Int32 Function(RacHandle, Pointer, IntPtr, + Pointer), + int Function( + RacHandle, Pointer, int, Pointer)>( + 'rac_voice_agent_process_voice_turn'); + +final void Function(Pointer)? + _voiceAgentResultFreeFn = (() { + try { + return _voiceAgentLib.lookupFunction< + Void Function(Pointer), + void Function(Pointer)>( + 'rac_voice_agent_result_free', + ); + } catch (_) { + return null; + } +})(); diff --git a/sdk/runanywhere-flutter/packages/runanywhere/lib/native/native_functions.dart b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/native_functions.dart new file mode 100644 index 000000000..e49326389 --- /dev/null +++ b/sdk/runanywhere-flutter/packages/runanywhere/lib/native/native_functions.dart @@ -0,0 +1,307 @@ +/// NativeFunctions +/// +/// Cached FFI function lookup registry. +/// +/// All [DynamicLibrary.lookupFunction] calls are performed once at first access +/// via lazy static fields. Subsequent calls return the cached function pointer, +/// avoiding repeated symbol-table searches (dlsym) on every invocation. +library native_functions; + +import 'dart:ffi'; + +import 'package:ffi/ffi.dart'; +import 'package:runanywhere/native/ffi_types.dart'; +import 'package:runanywhere/native/platform_loader.dart'; +import 'package:runanywhere/native/dart_bridge_vad.dart' as vad; + +/// Cached native function pointers for the RACommons library. +/// +/// Usage: +/// ```dart +/// final result = NativeFunctions.llmIsLoaded(_handle!); +/// ``` +abstract class NativeFunctions { + static final _lib = PlatformLoader.loadCommons(); + + // --------------------------------------------------------------------------- + // LLM Component + // --------------------------------------------------------------------------- + + static final int Function(Pointer) llmCreate = _lib.lookupFunction< + Int32 Function(Pointer), + int Function(Pointer)>('rac_llm_component_create'); + + static final int Function(RacHandle) llmIsLoaded = + _lib.lookupFunction( + 'rac_llm_component_is_loaded'); + + static final int Function(RacHandle) llmSupportsStreaming = + _lib.lookupFunction( + 'rac_llm_component_supports_streaming'); + + static final int Function( + RacHandle, Pointer, Pointer, Pointer) llmLoadModel = + _lib.lookupFunction< + Int32 Function( + RacHandle, Pointer, Pointer, Pointer), + int Function(RacHandle, Pointer, Pointer, + Pointer)>('rac_llm_component_load_model'); + + static final int Function(RacHandle) llmCleanup = + _lib.lookupFunction( + 'rac_llm_component_cleanup'); + + static final int Function(RacHandle) llmCancel = + _lib.lookupFunction( + 'rac_llm_component_cancel'); + + static final void Function(RacHandle) llmDestroy = + _lib.lookupFunction( + 'rac_llm_component_destroy'); + + // --------------------------------------------------------------------------- + // STT Component + // --------------------------------------------------------------------------- + + static final int Function(Pointer) sttCreate = _lib.lookupFunction< + Int32 Function(Pointer), + int Function(Pointer)>('rac_stt_component_create'); + + static final int Function(RacHandle) sttIsLoaded = + _lib.lookupFunction( + 'rac_stt_component_is_loaded'); + + static final int Function(RacHandle) sttSupportsStreaming = + _lib.lookupFunction( + 'rac_stt_component_supports_streaming'); + + static final int Function( + RacHandle, Pointer, Pointer, Pointer) sttLoadModel = + _lib.lookupFunction< + Int32 Function( + RacHandle, Pointer, Pointer, Pointer), + int Function(RacHandle, Pointer, Pointer, + Pointer)>('rac_stt_component_load_model'); + + static final int Function(RacHandle) sttCleanup = + _lib.lookupFunction( + 'rac_stt_component_cleanup'); + + static final void Function(Pointer) sttResultFree = + _lib.lookupFunction), + void Function(Pointer)>('rac_stt_result_free'); + + static final void Function(RacHandle) sttDestroy = + _lib.lookupFunction( + 'rac_stt_component_destroy'); + + // --------------------------------------------------------------------------- + // TTS Component + // --------------------------------------------------------------------------- + + static final int Function(Pointer) ttsCreate = _lib.lookupFunction< + Int32 Function(Pointer), + int Function(Pointer)>('rac_tts_component_create'); + + static final int Function(RacHandle) ttsIsLoaded = + _lib.lookupFunction( + 'rac_tts_component_is_loaded'); + + static final int Function( + RacHandle, Pointer, Pointer, Pointer) ttsLoadVoice = + _lib.lookupFunction< + Int32 Function( + RacHandle, Pointer, Pointer, Pointer), + int Function(RacHandle, Pointer, Pointer, + Pointer)>('rac_tts_component_load_voice'); + + static final int Function(RacHandle) ttsCleanup = + _lib.lookupFunction( + 'rac_tts_component_cleanup'); + + static final int Function(RacHandle) ttsStop = + _lib.lookupFunction( + 'rac_tts_component_stop'); + + static final void Function(RacHandle) ttsDestroy = + _lib.lookupFunction( + 'rac_tts_component_destroy'); + + // --------------------------------------------------------------------------- + // VAD Component + // --------------------------------------------------------------------------- + + static final int Function(Pointer) vadCreate = _lib.lookupFunction< + Int32 Function(Pointer), + int Function(Pointer)>('rac_vad_component_create'); + + static final int Function(RacHandle) vadIsInitialized = + _lib.lookupFunction( + 'rac_vad_component_is_initialized'); + + static final int Function(RacHandle) vadIsSpeechActive = + _lib.lookupFunction( + 'rac_vad_component_is_speech_active'); + + static final double Function(RacHandle) vadGetEnergyThreshold = _lib + .lookupFunction( + 'rac_vad_component_get_energy_threshold'); + + static final int Function(RacHandle, double) vadSetEnergyThreshold = + _lib.lookupFunction< + Int32 Function(RacHandle, Float), + int Function( + RacHandle, double)>('rac_vad_component_set_energy_threshold'); + + static final int Function(RacHandle) vadInitialize = + _lib.lookupFunction( + 'rac_vad_component_initialize'); + + static final int Function(RacHandle) vadStart = + _lib.lookupFunction( + 'rac_vad_component_start'); + + static final int Function(RacHandle) vadStop = + _lib.lookupFunction( + 'rac_vad_component_stop'); + + static final int Function(RacHandle) vadReset = + _lib.lookupFunction( + 'rac_vad_component_reset'); + + static final int Function(RacHandle) vadCleanup = + _lib.lookupFunction( + 'rac_vad_component_cleanup'); + + static final int Function( + RacHandle, + Pointer, + int, + Pointer, + ) vadProcess = _lib.lookupFunction< + Int32 Function( + RacHandle, + Pointer, + IntPtr, + Pointer, + ), + int Function( + RacHandle, + Pointer, + int, + Pointer, + )>('rac_vad_component_process'); + + static final void Function(RacHandle) vadDestroy = + _lib.lookupFunction( + 'rac_vad_component_destroy'); + + // --------------------------------------------------------------------------- + // VoiceAgent Component + // --------------------------------------------------------------------------- + + static final int Function( + RacHandle, + RacHandle, + RacHandle, + RacHandle, + Pointer, + ) voiceAgentCreate = _lib.lookupFunction< + Int32 Function( + RacHandle, + RacHandle, + RacHandle, + RacHandle, + Pointer, + ), + int Function( + RacHandle, + RacHandle, + RacHandle, + RacHandle, + Pointer, + )>('rac_voice_agent_create'); + + static final int Function(RacHandle, Pointer) voiceAgentIsReady = + _lib.lookupFunction), + int Function(RacHandle, Pointer)>('rac_voice_agent_is_ready'); + + static final int Function(RacHandle, Pointer) voiceAgentIsSTTLoaded = + _lib.lookupFunction< + Int32 Function(RacHandle, Pointer), + int Function( + RacHandle, Pointer)>('rac_voice_agent_is_stt_loaded'); + + static final int Function(RacHandle, Pointer) voiceAgentIsLLMLoaded = + _lib.lookupFunction< + Int32 Function(RacHandle, Pointer), + int Function( + RacHandle, Pointer)>('rac_voice_agent_is_llm_loaded'); + + static final int Function(RacHandle, Pointer) voiceAgentIsTTSLoaded = + _lib.lookupFunction< + Int32 Function(RacHandle, Pointer), + int Function( + RacHandle, Pointer)>('rac_voice_agent_is_tts_loaded'); + + static final int Function(RacHandle, Pointer, Pointer) + voiceAgentLoadSTTModel = _lib.lookupFunction< + Int32 Function(RacHandle, Pointer, Pointer), + int Function(RacHandle, Pointer, + Pointer)>('rac_voice_agent_load_stt_model'); + + static final int Function(RacHandle, Pointer, Pointer) + voiceAgentLoadLLMModel = _lib.lookupFunction< + Int32 Function(RacHandle, Pointer, Pointer), + int Function(RacHandle, Pointer, + Pointer)>('rac_voice_agent_load_llm_model'); + + static final int Function(RacHandle, Pointer, Pointer) + voiceAgentLoadTTSVoice = _lib.lookupFunction< + Int32 Function(RacHandle, Pointer, Pointer), + int Function(RacHandle, Pointer, + Pointer)>('rac_voice_agent_load_tts_voice'); + + static final int Function(RacHandle) voiceAgentInitializeWithLoadedModels = + _lib.lookupFunction( + 'rac_voice_agent_initialize_with_loaded_models'); + + static final int Function( + RacHandle, Pointer, int, Pointer>) + voiceAgentTranscribe = _lib.lookupFunction< + Int32 Function( + RacHandle, Pointer, IntPtr, Pointer>), + int Function(RacHandle, Pointer, int, + Pointer>)>('rac_voice_agent_transcribe'); + + static final int Function(RacHandle, Pointer, Pointer>) + voiceAgentGenerateResponse = _lib.lookupFunction< + Int32 Function(RacHandle, Pointer, Pointer>), + int Function(RacHandle, Pointer, + Pointer>)>('rac_voice_agent_generate_response'); + + static final int Function( + RacHandle, Pointer, Pointer>, Pointer) + voiceAgentSynthesizeSpeech = _lib.lookupFunction< + Int32 Function(RacHandle, Pointer, Pointer>, + Pointer), + int Function(RacHandle, Pointer, Pointer>, + Pointer)>('rac_voice_agent_synthesize_speech'); + + static final int Function(RacHandle) voiceAgentCleanup = + _lib.lookupFunction( + 'rac_voice_agent_cleanup'); + + static final void Function(RacHandle) voiceAgentDestroy = + _lib.lookupFunction( + 'rac_voice_agent_destroy'); + + static final void Function(Pointer)? racFree = (() { + try { + return _lib.lookupFunction), + void Function(Pointer)>('rac_free'); + } catch (_) { + return null; + } + })(); +}