@runanywhere/core 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/RunAnywhereCore.podspec +130 -0
- package/android/CMakeLists.txt +92 -0
- package/android/build.gradle +321 -0
- package/android/consumer-rules.pro +5 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/cpp/cpp-adapter.cpp +271 -0
- package/android/src/main/include/rac/backends/rac_llm_llamacpp.h +218 -0
- package/android/src/main/include/rac/backends/rac_stt_onnx.h +99 -0
- package/android/src/main/include/rac/backends/rac_stt_whispercpp.h +153 -0
- package/android/src/main/include/rac/backends/rac_tts_onnx.h +71 -0
- package/android/src/main/include/rac/backends/rac_vad_onnx.h +84 -0
- package/android/src/main/include/rac/core/capabilities/rac_lifecycle.h +290 -0
- package/android/src/main/include/rac/core/rac_analytics_events.h +610 -0
- package/android/src/main/include/rac/core/rac_audio_utils.h +88 -0
- package/android/src/main/include/rac/core/rac_component_types.h +160 -0
- package/android/src/main/include/rac/core/rac_core.h +331 -0
- package/android/src/main/include/rac/core/rac_error.h +469 -0
- package/android/src/main/include/rac/core/rac_events.h +334 -0
- package/android/src/main/include/rac/core/rac_logger.h +416 -0
- package/android/src/main/include/rac/core/rac_platform_adapter.h +340 -0
- package/android/src/main/include/rac/core/rac_sdk_state.h +292 -0
- package/android/src/main/include/rac/core/rac_structured_error.h +594 -0
- package/android/src/main/include/rac/core/rac_types.h +264 -0
- package/android/src/main/include/rac/features/llm/rac_llm.h +17 -0
- package/android/src/main/include/rac/features/llm/rac_llm_analytics.h +188 -0
- package/android/src/main/include/rac/features/llm/rac_llm_component.h +228 -0
- package/android/src/main/include/rac/features/llm/rac_llm_events.h +215 -0
- package/android/src/main/include/rac/features/llm/rac_llm_metrics.h +402 -0
- package/android/src/main/include/rac/features/llm/rac_llm_service.h +163 -0
- package/android/src/main/include/rac/features/llm/rac_llm_structured_output.h +141 -0
- package/android/src/main/include/rac/features/llm/rac_llm_types.h +384 -0
- package/android/src/main/include/rac/features/platform/rac_llm_platform.h +204 -0
- package/android/src/main/include/rac/features/platform/rac_tts_platform.h +197 -0
- package/android/src/main/include/rac/features/stt/rac_stt.h +17 -0
- package/android/src/main/include/rac/features/stt/rac_stt_analytics.h +204 -0
- package/android/src/main/include/rac/features/stt/rac_stt_component.h +162 -0
- package/android/src/main/include/rac/features/stt/rac_stt_events.h +62 -0
- package/android/src/main/include/rac/features/stt/rac_stt_service.h +154 -0
- package/android/src/main/include/rac/features/stt/rac_stt_types.h +389 -0
- package/android/src/main/include/rac/features/tts/rac_tts.h +17 -0
- package/android/src/main/include/rac/features/tts/rac_tts_analytics.h +181 -0
- package/android/src/main/include/rac/features/tts/rac_tts_component.h +158 -0
- package/android/src/main/include/rac/features/tts/rac_tts_events.h +54 -0
- package/android/src/main/include/rac/features/tts/rac_tts_service.h +162 -0
- package/android/src/main/include/rac/features/tts/rac_tts_types.h +374 -0
- package/android/src/main/include/rac/features/vad/rac_vad.h +17 -0
- package/android/src/main/include/rac/features/vad/rac_vad_analytics.h +236 -0
- package/android/src/main/include/rac/features/vad/rac_vad_component.h +185 -0
- package/android/src/main/include/rac/features/vad/rac_vad_energy.h +443 -0
- package/android/src/main/include/rac/features/vad/rac_vad_events.h +76 -0
- package/android/src/main/include/rac/features/vad/rac_vad_service.h +167 -0
- package/android/src/main/include/rac/features/vad/rac_vad_types.h +244 -0
- package/android/src/main/include/rac/features/voice_agent/rac_voice_agent.h +612 -0
- package/android/src/main/include/rac/infrastructure/device/rac_device_manager.h +176 -0
- package/android/src/main/include/rac/infrastructure/download/rac_download.h +418 -0
- package/android/src/main/include/rac/infrastructure/events/rac_events.h +177 -0
- package/android/src/main/include/rac/infrastructure/model_management/rac_model_assignment.h +169 -0
- package/android/src/main/include/rac/infrastructure/model_management/rac_model_paths.h +258 -0
- package/android/src/main/include/rac/infrastructure/model_management/rac_model_registry.h +357 -0
- package/android/src/main/include/rac/infrastructure/model_management/rac_model_strategy.h +374 -0
- package/android/src/main/include/rac/infrastructure/model_management/rac_model_types.h +613 -0
- package/android/src/main/include/rac/infrastructure/network/rac_api_types.h +335 -0
- package/android/src/main/include/rac/infrastructure/network/rac_auth_manager.h +252 -0
- package/android/src/main/include/rac/infrastructure/network/rac_dev_config.h +85 -0
- package/android/src/main/include/rac/infrastructure/network/rac_endpoints.h +102 -0
- package/android/src/main/include/rac/infrastructure/network/rac_environment.h +220 -0
- package/android/src/main/include/rac/infrastructure/network/rac_http_client.h +233 -0
- package/android/src/main/include/rac/infrastructure/storage/rac_storage_analyzer.h +286 -0
- package/android/src/main/include/rac/infrastructure/telemetry/rac_telemetry_manager.h +206 -0
- package/android/src/main/include/rac/infrastructure/telemetry/rac_telemetry_types.h +234 -0
- package/android/src/main/java/com/margelo/nitro/runanywhere/ArchiveUtility.kt +308 -0
- package/android/src/main/java/com/margelo/nitro/runanywhere/HybridRunAnywhereDeviceInfo.kt +229 -0
- package/android/src/main/java/com/margelo/nitro/runanywhere/PlatformAdapterBridge.kt +392 -0
- package/android/src/main/java/com/margelo/nitro/runanywhere/RunAnywhereCorePackage.kt +28 -0
- package/android/src/main/java/com/margelo/nitro/runanywhere/SDKLogger.kt +357 -0
- package/android/src/main/java/com/margelo/nitro/runanywhere/SecureStorageManager.kt +147 -0
- package/android/src/main/jniLibs/arm64-v8a/libc++_shared.so +0 -0
- package/android/src/main/jniLibs/arm64-v8a/libomp.so +0 -0
- package/android/src/main/jniLibs/arm64-v8a/librac_commons.so +0 -0
- package/android/src/main/jniLibs/arm64-v8a/librunanywhere_jni.so +0 -0
- package/cpp/HybridRunAnywhereCore.cpp +2505 -0
- package/cpp/HybridRunAnywhereCore.hpp +271 -0
- package/cpp/bridges/AuthBridge.cpp +209 -0
- package/cpp/bridges/AuthBridge.hpp +157 -0
- package/cpp/bridges/DeviceBridge.cpp +257 -0
- package/cpp/bridges/DeviceBridge.hpp +155 -0
- package/cpp/bridges/DownloadBridge.cpp +299 -0
- package/cpp/bridges/DownloadBridge.hpp +197 -0
- package/cpp/bridges/EventBridge.cpp +125 -0
- package/cpp/bridges/EventBridge.hpp +139 -0
- package/cpp/bridges/HTTPBridge.cpp +96 -0
- package/cpp/bridges/HTTPBridge.hpp +144 -0
- package/cpp/bridges/InitBridge.cpp +1273 -0
- package/cpp/bridges/InitBridge.hpp +306 -0
- package/cpp/bridges/ModelRegistryBridge.cpp +394 -0
- package/cpp/bridges/ModelRegistryBridge.hpp +177 -0
- package/cpp/bridges/StorageBridge.cpp +269 -0
- package/cpp/bridges/StorageBridge.hpp +172 -0
- package/cpp/bridges/TelemetryBridge.cpp +352 -0
- package/cpp/bridges/TelemetryBridge.hpp +126 -0
- package/ios/.testlocal +0 -0
- package/ios/ArchiveUtility.swift +526 -0
- package/ios/ArchiveUtilityBridge.m +52 -0
- package/ios/AudioDecoder.h +38 -0
- package/ios/AudioDecoder.m +162 -0
- package/ios/Binaries/RACommons.xcframework/Info.plist +44 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/RACommons.h +67 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_analytics_events.h +610 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_api_types.h +335 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_audio_utils.h +88 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_auth_manager.h +252 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_component_types.h +160 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_core.h +331 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_dev_config.h +85 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_device_manager.h +176 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_download.h +418 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_endpoints.h +102 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_environment.h +220 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_error.h +469 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_events.h +177 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_http_client.h +233 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_lifecycle.h +290 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm.h +17 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_analytics.h +188 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_component.h +228 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_events.h +215 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_llamacpp.h +218 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_metrics.h +402 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_platform.h +204 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_service.h +163 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_structured_output.h +141 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_types.h +384 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_logger.h +416 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_model_assignment.h +169 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_model_paths.h +258 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_model_registry.h +357 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_model_strategy.h +374 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_model_types.h +613 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_platform_adapter.h +340 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_sdk_state.h +292 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_storage_analyzer.h +286 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_structured_error.h +594 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_stt.h +17 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_stt_analytics.h +204 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_stt_component.h +162 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_stt_events.h +62 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_stt_onnx.h +99 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_stt_service.h +154 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_stt_types.h +389 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_stt_whispercpp.h +153 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_telemetry_manager.h +206 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_telemetry_types.h +234 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tts.h +17 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tts_analytics.h +181 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tts_component.h +158 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tts_events.h +54 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tts_onnx.h +71 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tts_platform.h +197 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tts_service.h +162 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tts_types.h +374 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_types.h +264 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad.h +17 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad_analytics.h +236 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad_component.h +185 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad_energy.h +443 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad_events.h +76 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad_onnx.h +84 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad_service.h +167 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad_types.h +244 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_voice_agent.h +612 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Info.plist +11 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Modules/module.modulemap +5 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/RACommons +0 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/RACommons.h +67 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_analytics_events.h +610 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_api_types.h +335 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_audio_utils.h +88 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_auth_manager.h +252 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_component_types.h +160 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_core.h +331 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_dev_config.h +85 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_device_manager.h +176 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_download.h +418 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_endpoints.h +102 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_environment.h +220 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_error.h +469 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_events.h +177 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_http_client.h +233 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_lifecycle.h +290 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm.h +17 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_analytics.h +188 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_component.h +228 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_events.h +215 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_llamacpp.h +218 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_metrics.h +402 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_platform.h +204 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_service.h +163 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_structured_output.h +141 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_types.h +384 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_logger.h +416 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_model_assignment.h +169 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_model_paths.h +258 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_model_registry.h +357 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_model_strategy.h +374 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_model_types.h +613 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_platform_adapter.h +340 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_sdk_state.h +292 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_storage_analyzer.h +286 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_structured_error.h +594 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_stt.h +17 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_stt_analytics.h +204 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_stt_component.h +162 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_stt_events.h +62 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_stt_onnx.h +99 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_stt_service.h +154 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_stt_types.h +389 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_stt_whispercpp.h +153 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_telemetry_manager.h +206 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_telemetry_types.h +234 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tts.h +17 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tts_analytics.h +181 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tts_component.h +158 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tts_events.h +54 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tts_onnx.h +71 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tts_platform.h +197 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tts_service.h +162 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tts_types.h +374 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_types.h +264 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad.h +17 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad_analytics.h +236 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad_component.h +185 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad_energy.h +443 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad_events.h +76 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad_onnx.h +84 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad_service.h +167 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad_types.h +244 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_voice_agent.h +612 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Info.plist +11 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Modules/module.modulemap +5 -0
- package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/RACommons +0 -0
- package/ios/HybridRunAnywhereDeviceInfo.swift +214 -0
- package/ios/KeychainManager.swift +116 -0
- package/ios/PlatformAdapter.swift +100 -0
- package/ios/PlatformAdapterBridge.h +152 -0
- package/ios/PlatformAdapterBridge.m +570 -0
- package/ios/RNSDKLoggerBridge.h +41 -0
- package/ios/RNSDKLoggerBridge.m +66 -0
- package/ios/SDKLogger.swift +329 -0
- package/nitro.json +20 -0
- package/nitrogen/generated/.gitattributes +1 -0
- package/nitrogen/generated/android/c++/JHybridRunAnywhereDeviceInfoSpec.cpp +257 -0
- package/nitrogen/generated/android/c++/JHybridRunAnywhereDeviceInfoSpec.hpp +77 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/runanywhere/HybridRunAnywhereDeviceInfoSpec.kt +106 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/runanywhere/runanywherecoreOnLoad.kt +35 -0
- package/nitrogen/generated/android/runanywherecore+autolinking.cmake +82 -0
- package/nitrogen/generated/android/runanywherecore+autolinking.gradle +27 -0
- package/nitrogen/generated/android/runanywherecoreOnLoad.cpp +54 -0
- package/nitrogen/generated/android/runanywherecoreOnLoad.hpp +25 -0
- package/nitrogen/generated/ios/RunAnywhereCore+autolinking.rb +60 -0
- package/nitrogen/generated/ios/RunAnywhereCore-Swift-Cxx-Bridge.cpp +65 -0
- package/nitrogen/generated/ios/RunAnywhereCore-Swift-Cxx-Bridge.hpp +197 -0
- package/nitrogen/generated/ios/RunAnywhereCore-Swift-Cxx-Umbrella.hpp +45 -0
- package/nitrogen/generated/ios/RunAnywhereCoreAutolinking.mm +43 -0
- package/nitrogen/generated/ios/RunAnywhereCoreAutolinking.swift +25 -0
- package/nitrogen/generated/ios/c++/HybridRunAnywhereDeviceInfoSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridRunAnywhereDeviceInfoSpecSwift.hpp +173 -0
- package/nitrogen/generated/ios/swift/Func_void_bool.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_double.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridRunAnywhereDeviceInfoSpec.swift +68 -0
- package/nitrogen/generated/ios/swift/HybridRunAnywhereDeviceInfoSpec_cxx.swift +366 -0
- package/nitrogen/generated/shared/c++/HybridRunAnywhereCoreSpec.cpp +92 -0
- package/nitrogen/generated/shared/c++/HybridRunAnywhereCoreSpec.hpp +138 -0
- package/nitrogen/generated/shared/c++/HybridRunAnywhereDeviceInfoSpec.cpp +33 -0
- package/nitrogen/generated/shared/c++/HybridRunAnywhereDeviceInfoSpec.hpp +75 -0
- package/package.json +76 -0
- package/react-native.config.js +14 -0
- package/src/Features/VoiceSession/AudioCaptureManager.ts +286 -0
- package/src/Features/VoiceSession/AudioPlaybackManager.ts +300 -0
- package/src/Features/VoiceSession/VoiceSessionHandle.ts +530 -0
- package/src/Features/VoiceSession/index.ts +20 -0
- package/src/Features/index.ts +7 -0
- package/src/Foundation/Constants/SDKConstants.ts +47 -0
- package/src/Foundation/Constants/index.ts +8 -0
- package/src/Foundation/DependencyInjection/ServiceContainer.ts +154 -0
- package/src/Foundation/DependencyInjection/ServiceRegistry.ts +51 -0
- package/src/Foundation/DependencyInjection/index.ts +6 -0
- package/src/Foundation/ErrorTypes/ErrorCategory.ts +184 -0
- package/src/Foundation/ErrorTypes/ErrorCodes.ts +151 -0
- package/src/Foundation/ErrorTypes/ErrorContext.ts +201 -0
- package/src/Foundation/ErrorTypes/SDKError.ts +507 -0
- package/src/Foundation/ErrorTypes/index.ts +57 -0
- package/src/Foundation/Initialization/InitializationPhase.ts +85 -0
- package/src/Foundation/Initialization/InitializationState.ts +168 -0
- package/src/Foundation/Initialization/index.ts +26 -0
- package/src/Foundation/Logging/Destinations/NativeLogBridge.ts +147 -0
- package/src/Foundation/Logging/Destinations/SentryDestination.ts +209 -0
- package/src/Foundation/Logging/Logger/SDKLogger.ts +232 -0
- package/src/Foundation/Logging/Models/LogLevel.ts +36 -0
- package/src/Foundation/Logging/Models/LoggingConfiguration.ts +117 -0
- package/src/Foundation/Logging/Services/LoggingManager.ts +407 -0
- package/src/Foundation/Logging/index.ts +59 -0
- package/src/Foundation/Security/DeviceIdentity.ts +92 -0
- package/src/Foundation/Security/SecureStorageError.ts +132 -0
- package/src/Foundation/Security/SecureStorageKeys.ts +35 -0
- package/src/Foundation/Security/SecureStorageService.ts +449 -0
- package/src/Foundation/Security/index.ts +17 -0
- package/src/Foundation/index.ts +26 -0
- package/src/Infrastructure/Events/EventPublisher.ts +165 -0
- package/src/Infrastructure/Events/SDKEvent.ts +214 -0
- package/src/Infrastructure/Events/index.ts +15 -0
- package/src/Infrastructure/index.ts +9 -0
- package/src/Public/Events/EventBus.ts +488 -0
- package/src/Public/Events/index.ts +8 -0
- package/src/Public/Extensions/RunAnywhere+Logging.ts +51 -0
- package/src/Public/Extensions/RunAnywhere+Models.ts +392 -0
- package/src/Public/Extensions/RunAnywhere+STT.ts +424 -0
- package/src/Public/Extensions/RunAnywhere+Storage.ts +151 -0
- package/src/Public/Extensions/RunAnywhere+StructuredOutput.ts +316 -0
- package/src/Public/Extensions/RunAnywhere+TTS.ts +430 -0
- package/src/Public/Extensions/RunAnywhere+TextGeneration.ts +320 -0
- package/src/Public/Extensions/RunAnywhere+VAD.ts +359 -0
- package/src/Public/Extensions/RunAnywhere+VoiceAgent.ts +225 -0
- package/src/Public/Extensions/RunAnywhere+VoiceSession.ts +155 -0
- package/src/Public/Extensions/index.ts +126 -0
- package/src/Public/RunAnywhere.ts +695 -0
- package/src/index.ts +238 -0
- package/src/native/NativeRunAnywhereCore.ts +291 -0
- package/src/native/NativeRunAnywhereModule.ts +32 -0
- package/src/native/index.ts +21 -0
- package/src/services/DownloadService.ts +282 -0
- package/src/services/FileSystem.ts +810 -0
- package/src/services/ModelRegistry.ts +246 -0
- package/src/services/Network/APIEndpoints.ts +84 -0
- package/src/services/Network/HTTPService.ts +479 -0
- package/src/services/Network/NetworkConfiguration.ts +130 -0
- package/src/services/Network/TelemetryService.ts +318 -0
- package/src/services/Network/index.ts +29 -0
- package/src/services/SystemTTSService.ts +130 -0
- package/src/services/index.ts +66 -0
- package/src/specs/RunAnywhereCore.nitro.ts +627 -0
- package/src/specs/RunAnywhereDeviceInfo.nitro.ts +73 -0
- package/src/types/LLMTypes.ts +127 -0
- package/src/types/STTTypes.ts +124 -0
- package/src/types/StructuredOutputTypes.ts +156 -0
- package/src/types/TTSTypes.ts +126 -0
- package/src/types/VADTypes.ts +70 -0
- package/src/types/VoiceAgentTypes.ts +182 -0
- package/src/types/enums.ts +258 -0
- package/src/types/events.ts +337 -0
- package/src/types/external.d.ts +142 -0
- package/src/types/index.ts +146 -0
- package/src/types/models.ts +579 -0
|
@@ -0,0 +1,2505 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HybridRunAnywhereCore.cpp
|
|
3
|
+
*
|
|
4
|
+
* Nitrogen HybridObject implementation for RunAnywhere Core SDK.
|
|
5
|
+
*
|
|
6
|
+
* Core SDK implementation - includes:
|
|
7
|
+
* - SDK Lifecycle, Authentication, Device Registration
|
|
8
|
+
* - Model Registry, Download Service, Storage
|
|
9
|
+
* - Events, HTTP Client, Utilities
|
|
10
|
+
* - LLM/STT/TTS/VAD/VoiceAgent capabilities (backend-agnostic)
|
|
11
|
+
*
|
|
12
|
+
* The capability methods (LLM, STT, TTS, VAD, VoiceAgent) are BACKEND-AGNOSTIC.
|
|
13
|
+
* They call the C++ rac_*_component_* APIs which work with any registered backend.
|
|
14
|
+
* Apps must install a backend package to register the actual implementation:
|
|
15
|
+
* - @runanywhere/llamacpp registers the LLM backend via rac_backend_llamacpp_register()
|
|
16
|
+
* - @runanywhere/onnx registers the STT/TTS/VAD backends via rac_backend_onnx_register()
|
|
17
|
+
*
|
|
18
|
+
* Mirrors Swift's CppBridge architecture from:
|
|
19
|
+
* sdk/runanywhere-swift/Sources/RunAnywhere/Foundation/Bridge/
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
#include "HybridRunAnywhereCore.hpp"
|
|
24
|
+
|
|
25
|
+
// RACommons headers
|
|
26
|
+
#include "rac_dev_config.h" // For rac_dev_config_get_build_token
|
|
27
|
+
|
|
28
|
+
// Core bridges - aligned with actual RACommons API
|
|
29
|
+
#include "bridges/InitBridge.hpp"
|
|
30
|
+
#include "bridges/DeviceBridge.hpp"
|
|
31
|
+
#include "bridges/AuthBridge.hpp"
|
|
32
|
+
#include "bridges/StorageBridge.hpp"
|
|
33
|
+
#include "bridges/ModelRegistryBridge.hpp"
|
|
34
|
+
#include "bridges/EventBridge.hpp"
|
|
35
|
+
#include "bridges/HTTPBridge.hpp"
|
|
36
|
+
#include "bridges/DownloadBridge.hpp"
|
|
37
|
+
#include "bridges/TelemetryBridge.hpp"
|
|
38
|
+
|
|
39
|
+
// RACommons C API headers for capability methods
|
|
40
|
+
// These are backend-agnostic - they work with any registered backend
|
|
41
|
+
#include "rac_core.h"
|
|
42
|
+
#include "rac_llm_component.h"
|
|
43
|
+
#include "rac_llm_types.h"
|
|
44
|
+
#include "rac_llm_structured_output.h"
|
|
45
|
+
#include "rac_stt_component.h"
|
|
46
|
+
#include "rac_stt_types.h"
|
|
47
|
+
#include "rac_tts_component.h"
|
|
48
|
+
#include "rac_tts_types.h"
|
|
49
|
+
#include "rac_vad_component.h"
|
|
50
|
+
#include "rac_vad_types.h"
|
|
51
|
+
#include "rac_voice_agent.h"
|
|
52
|
+
#include "rac_types.h"
|
|
53
|
+
#include "rac_model_assignment.h"
|
|
54
|
+
|
|
55
|
+
#include <sstream>
|
|
56
|
+
#include <chrono>
|
|
57
|
+
#include <vector>
|
|
58
|
+
#include <mutex>
|
|
59
|
+
#include <cstdio>
|
|
60
|
+
#include <cstring>
|
|
61
|
+
|
|
62
|
+
// Platform-specific headers for memory usage
|
|
63
|
+
#if defined(__APPLE__)
|
|
64
|
+
#include <mach/mach.h>
|
|
65
|
+
#include <mach/task.h>
|
|
66
|
+
#endif
|
|
67
|
+
|
|
68
|
+
// Platform-specific logging
|
|
69
|
+
#if defined(ANDROID) || defined(__ANDROID__)
|
|
70
|
+
#include <android/log.h>
|
|
71
|
+
#define LOG_TAG "HybridRunAnywhereCore"
|
|
72
|
+
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
|
|
73
|
+
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
|
74
|
+
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
|
75
|
+
#else
|
|
76
|
+
#define LOGI(...) printf("[HybridRunAnywhereCore] "); printf(__VA_ARGS__); printf("\n")
|
|
77
|
+
#define LOGE(...) printf("[HybridRunAnywhereCore ERROR] "); printf(__VA_ARGS__); printf("\n")
|
|
78
|
+
#define LOGD(...) printf("[HybridRunAnywhereCore DEBUG] "); printf(__VA_ARGS__); printf("\n")
|
|
79
|
+
#endif
|
|
80
|
+
|
|
81
|
+
namespace margelo::nitro::runanywhere {
|
|
82
|
+
|
|
83
|
+
using namespace ::runanywhere::bridges;
|
|
84
|
+
|
|
85
|
+
// ============================================================================
|
|
86
|
+
// Base64 Utilities
|
|
87
|
+
// ============================================================================
|
|
88
|
+
|
|
89
|
+
namespace {
|
|
90
|
+
|
|
91
|
+
static const std::string base64_chars =
|
|
92
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
93
|
+
|
|
94
|
+
std::vector<uint8_t> base64Decode(const std::string& encoded) {
|
|
95
|
+
std::vector<uint8_t> decoded;
|
|
96
|
+
if (encoded.empty()) return decoded;
|
|
97
|
+
|
|
98
|
+
int val = 0, valb = -8;
|
|
99
|
+
for (char c : encoded) {
|
|
100
|
+
if (c == '=' || c == '\n' || c == '\r') continue;
|
|
101
|
+
size_t pos = base64_chars.find(c);
|
|
102
|
+
if (pos == std::string::npos) continue;
|
|
103
|
+
val = (val << 6) + static_cast<int>(pos);
|
|
104
|
+
valb += 6;
|
|
105
|
+
if (valb >= 0) {
|
|
106
|
+
decoded.push_back(static_cast<uint8_t>((val >> valb) & 0xFF));
|
|
107
|
+
valb -= 8;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return decoded;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
std::string base64Encode(const uint8_t* data, size_t len) {
|
|
114
|
+
std::string encoded;
|
|
115
|
+
if (!data || len == 0) return encoded;
|
|
116
|
+
|
|
117
|
+
int val = 0, valb = -6;
|
|
118
|
+
for (size_t i = 0; i < len; i++) {
|
|
119
|
+
val = (val << 8) + data[i];
|
|
120
|
+
valb += 8;
|
|
121
|
+
while (valb >= 0) {
|
|
122
|
+
encoded.push_back(base64_chars[(val >> valb) & 0x3F]);
|
|
123
|
+
valb -= 6;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (valb > -6) {
|
|
127
|
+
encoded.push_back(base64_chars[((val << 8) >> (valb + 8)) & 0x3F]);
|
|
128
|
+
}
|
|
129
|
+
while (encoded.size() % 4) {
|
|
130
|
+
encoded.push_back('=');
|
|
131
|
+
}
|
|
132
|
+
return encoded;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ============================================================================
|
|
136
|
+
// JSON Utilities
|
|
137
|
+
// ============================================================================
|
|
138
|
+
|
|
139
|
+
int extractIntValue(const std::string& json, const std::string& key, int defaultValue) {
|
|
140
|
+
std::string searchKey = "\"" + key + "\":";
|
|
141
|
+
size_t pos = json.find(searchKey);
|
|
142
|
+
if (pos == std::string::npos) return defaultValue;
|
|
143
|
+
pos += searchKey.length();
|
|
144
|
+
while (pos < json.size() && (json[pos] == ' ' || json[pos] == '\t')) pos++;
|
|
145
|
+
if (pos >= json.size()) return defaultValue;
|
|
146
|
+
// Skip if this is a string value (starts with quote)
|
|
147
|
+
if (json[pos] == '"') return defaultValue;
|
|
148
|
+
// Try to parse as integer, return default on failure
|
|
149
|
+
try {
|
|
150
|
+
return std::stoi(json.substr(pos));
|
|
151
|
+
} catch (...) {
|
|
152
|
+
return defaultValue;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
double extractDoubleValue(const std::string& json, const std::string& key, double defaultValue) {
|
|
157
|
+
std::string searchKey = "\"" + key + "\":";
|
|
158
|
+
size_t pos = json.find(searchKey);
|
|
159
|
+
if (pos == std::string::npos) return defaultValue;
|
|
160
|
+
pos += searchKey.length();
|
|
161
|
+
while (pos < json.size() && (json[pos] == ' ' || json[pos] == '\t')) pos++;
|
|
162
|
+
if (pos >= json.size()) return defaultValue;
|
|
163
|
+
// Skip if this is a string value (starts with quote)
|
|
164
|
+
if (json[pos] == '"') return defaultValue;
|
|
165
|
+
// Try to parse as double, return default on failure
|
|
166
|
+
try {
|
|
167
|
+
return std::stod(json.substr(pos));
|
|
168
|
+
} catch (...) {
|
|
169
|
+
return defaultValue;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
std::string extractStringValue(const std::string& json, const std::string& key, const std::string& defaultValue = "") {
|
|
174
|
+
std::string searchKey = "\"" + key + "\":\"";
|
|
175
|
+
size_t pos = json.find(searchKey);
|
|
176
|
+
if (pos == std::string::npos) return defaultValue;
|
|
177
|
+
pos += searchKey.length();
|
|
178
|
+
size_t endPos = json.find("\"", pos);
|
|
179
|
+
if (endPos == std::string::npos) return defaultValue;
|
|
180
|
+
return json.substr(pos, endPos - pos);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
bool extractBoolValue(const std::string& json, const std::string& key, bool defaultValue = false) {
|
|
184
|
+
std::string searchKey = "\"" + key + "\":";
|
|
185
|
+
size_t pos = json.find(searchKey);
|
|
186
|
+
if (pos == std::string::npos) return defaultValue;
|
|
187
|
+
pos += searchKey.length();
|
|
188
|
+
while (pos < json.size() && (json[pos] == ' ' || json[pos] == '\t')) pos++;
|
|
189
|
+
if (pos >= json.size()) return defaultValue;
|
|
190
|
+
if (json.substr(pos, 4) == "true") return true;
|
|
191
|
+
if (json.substr(pos, 5) == "false") return false;
|
|
192
|
+
return defaultValue;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Convert TypeScript framework string to C++ enum
|
|
196
|
+
rac_inference_framework_t frameworkFromString(const std::string& framework) {
|
|
197
|
+
if (framework == "LlamaCpp" || framework == "llamacpp") return RAC_FRAMEWORK_LLAMACPP;
|
|
198
|
+
if (framework == "ONNX" || framework == "onnx") return RAC_FRAMEWORK_ONNX;
|
|
199
|
+
if (framework == "FoundationModels") return RAC_FRAMEWORK_FOUNDATION_MODELS;
|
|
200
|
+
if (framework == "SystemTTS") return RAC_FRAMEWORK_SYSTEM_TTS;
|
|
201
|
+
return RAC_FRAMEWORK_UNKNOWN;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Convert TypeScript category string to C++ enum
|
|
205
|
+
rac_model_category_t categoryFromString(const std::string& category) {
|
|
206
|
+
if (category == "Language" || category == "language") return RAC_MODEL_CATEGORY_LANGUAGE;
|
|
207
|
+
// Handle both hyphen and underscore variants
|
|
208
|
+
if (category == "SpeechRecognition" || category == "speech-recognition" || category == "speech_recognition") return RAC_MODEL_CATEGORY_SPEECH_RECOGNITION;
|
|
209
|
+
if (category == "SpeechSynthesis" || category == "speech-synthesis" || category == "speech_synthesis") return RAC_MODEL_CATEGORY_SPEECH_SYNTHESIS;
|
|
210
|
+
if (category == "VoiceActivity" || category == "voice-activity" || category == "voice_activity") return RAC_MODEL_CATEGORY_AUDIO;
|
|
211
|
+
if (category == "Vision" || category == "vision") return RAC_MODEL_CATEGORY_VISION;
|
|
212
|
+
if (category == "ImageGeneration" || category == "image-generation" || category == "image_generation") return RAC_MODEL_CATEGORY_IMAGE_GENERATION;
|
|
213
|
+
if (category == "Multimodal" || category == "multimodal") return RAC_MODEL_CATEGORY_MULTIMODAL;
|
|
214
|
+
if (category == "Audio" || category == "audio") return RAC_MODEL_CATEGORY_AUDIO;
|
|
215
|
+
return RAC_MODEL_CATEGORY_UNKNOWN;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Convert TypeScript format string to C++ enum
|
|
219
|
+
rac_model_format_t formatFromString(const std::string& format) {
|
|
220
|
+
if (format == "GGUF" || format == "gguf") return RAC_MODEL_FORMAT_GGUF;
|
|
221
|
+
if (format == "GGML" || format == "ggml") return RAC_MODEL_FORMAT_BIN; // GGML -> BIN as fallback
|
|
222
|
+
if (format == "ONNX" || format == "onnx") return RAC_MODEL_FORMAT_ONNX;
|
|
223
|
+
if (format == "ORT" || format == "ort") return RAC_MODEL_FORMAT_ORT;
|
|
224
|
+
if (format == "BIN" || format == "bin") return RAC_MODEL_FORMAT_BIN;
|
|
225
|
+
return RAC_MODEL_FORMAT_UNKNOWN;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
std::string jsonString(const std::string& value) {
|
|
229
|
+
std::string escaped = "\"";
|
|
230
|
+
for (char c : value) {
|
|
231
|
+
if (c == '"') escaped += "\\\"";
|
|
232
|
+
else if (c == '\\') escaped += "\\\\";
|
|
233
|
+
else if (c == '\n') escaped += "\\n";
|
|
234
|
+
else if (c == '\r') escaped += "\\r";
|
|
235
|
+
else if (c == '\t') escaped += "\\t";
|
|
236
|
+
else escaped += c;
|
|
237
|
+
}
|
|
238
|
+
escaped += "\"";
|
|
239
|
+
return escaped;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
std::string buildJsonObject(const std::vector<std::pair<std::string, std::string>>& keyValues) {
|
|
243
|
+
std::string result = "{";
|
|
244
|
+
for (size_t i = 0; i < keyValues.size(); i++) {
|
|
245
|
+
if (i > 0) result += ",";
|
|
246
|
+
result += "\"" + keyValues[i].first + "\":" + keyValues[i].second;
|
|
247
|
+
}
|
|
248
|
+
result += "}";
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
} // anonymous namespace
|
|
253
|
+
|
|
254
|
+
// ============================================================================
|
|
255
|
+
// Constructor / Destructor
|
|
256
|
+
// ============================================================================
|
|
257
|
+
|
|
258
|
+
HybridRunAnywhereCore::HybridRunAnywhereCore() : HybridObject(TAG) {
|
|
259
|
+
LOGI("HybridRunAnywhereCore constructor - core module");
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
HybridRunAnywhereCore::~HybridRunAnywhereCore() {
|
|
263
|
+
LOGI("HybridRunAnywhereCore destructor");
|
|
264
|
+
|
|
265
|
+
// Cleanup bridges (note: telemetry is NOT shutdown here because it's shared
|
|
266
|
+
// across instances and should persist for the SDK lifetime)
|
|
267
|
+
EventBridge::shared().unregisterFromEvents();
|
|
268
|
+
DownloadBridge::shared().shutdown();
|
|
269
|
+
StorageBridge::shared().shutdown();
|
|
270
|
+
ModelRegistryBridge::shared().shutdown();
|
|
271
|
+
// Note: InitBridge and TelemetryBridge are not shutdown in destructor
|
|
272
|
+
// to allow events to be tracked even after HybridObject instances are destroyed
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ============================================================================
|
|
276
|
+
// SDK Lifecycle
|
|
277
|
+
// ============================================================================
|
|
278
|
+
|
|
279
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::initialize(
|
|
280
|
+
const std::string& configJson) {
|
|
281
|
+
return Promise<bool>::async([this, configJson]() {
|
|
282
|
+
std::lock_guard<std::mutex> lock(initMutex_);
|
|
283
|
+
|
|
284
|
+
LOGI("Initializing Core SDK...");
|
|
285
|
+
|
|
286
|
+
// Parse config
|
|
287
|
+
std::string apiKey = extractStringValue(configJson, "apiKey");
|
|
288
|
+
std::string baseURL = extractStringValue(configJson, "baseURL", "https://api.runanywhere.ai");
|
|
289
|
+
std::string deviceId = extractStringValue(configJson, "deviceId");
|
|
290
|
+
std::string envStr = extractStringValue(configJson, "environment", "production");
|
|
291
|
+
std::string sdkVersionFromConfig = extractStringValue(configJson, "sdkVersion", "0.2.0");
|
|
292
|
+
|
|
293
|
+
// Determine environment
|
|
294
|
+
SDKEnvironment env = SDKEnvironment::Production;
|
|
295
|
+
if (envStr == "development") env = SDKEnvironment::Development;
|
|
296
|
+
else if (envStr == "staging") env = SDKEnvironment::Staging;
|
|
297
|
+
|
|
298
|
+
// 1. Initialize core (platform adapter + state)
|
|
299
|
+
rac_result_t result = InitBridge::shared().initialize(env, apiKey, baseURL, deviceId);
|
|
300
|
+
if (result != RAC_SUCCESS) {
|
|
301
|
+
setLastError("Failed to initialize SDK core: " + std::to_string(result));
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Set SDK version from TypeScript SDKConstants (centralized version)
|
|
306
|
+
InitBridge::shared().setSdkVersion(sdkVersionFromConfig);
|
|
307
|
+
|
|
308
|
+
// 2. Set base directory for model paths (mirrors Swift's CppBridge.ModelPaths.setBaseDirectory)
|
|
309
|
+
// This must be called before using model path utilities
|
|
310
|
+
std::string documentsPath = extractStringValue(configJson, "documentsPath");
|
|
311
|
+
if (!documentsPath.empty()) {
|
|
312
|
+
result = InitBridge::shared().setBaseDirectory(documentsPath);
|
|
313
|
+
if (result != RAC_SUCCESS) {
|
|
314
|
+
LOGE("Failed to set base directory: %d", result);
|
|
315
|
+
// Continue - not fatal, but model paths may not work correctly
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
LOGE("documentsPath not provided in config - model paths may not work correctly!");
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// 3. Initialize model registry
|
|
322
|
+
result = ModelRegistryBridge::shared().initialize();
|
|
323
|
+
if (result != RAC_SUCCESS) {
|
|
324
|
+
LOGE("Failed to initialize model registry: %d", result);
|
|
325
|
+
// Continue - not fatal
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// 4. Initialize storage analyzer
|
|
329
|
+
result = StorageBridge::shared().initialize();
|
|
330
|
+
if (result != RAC_SUCCESS) {
|
|
331
|
+
LOGE("Failed to initialize storage analyzer: %d", result);
|
|
332
|
+
// Continue - not fatal
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// 5. Initialize download manager
|
|
336
|
+
result = DownloadBridge::shared().initialize();
|
|
337
|
+
if (result != RAC_SUCCESS) {
|
|
338
|
+
LOGE("Failed to initialize download manager: %d", result);
|
|
339
|
+
// Continue - not fatal
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// 6. Register for events
|
|
343
|
+
EventBridge::shared().registerForEvents();
|
|
344
|
+
|
|
345
|
+
// 7. Configure HTTP
|
|
346
|
+
HTTPBridge::shared().configure(baseURL, apiKey);
|
|
347
|
+
|
|
348
|
+
// 8. Initialize telemetry (matches Swift's CppBridge.Telemetry.initialize)
|
|
349
|
+
// This creates the C++ telemetry manager and registers HTTP callback
|
|
350
|
+
{
|
|
351
|
+
std::string persistentDeviceId = InitBridge::shared().getPersistentDeviceUUID();
|
|
352
|
+
std::string deviceModel = InitBridge::shared().getDeviceModel();
|
|
353
|
+
std::string osVersion = InitBridge::shared().getOSVersion();
|
|
354
|
+
|
|
355
|
+
if (!persistentDeviceId.empty()) {
|
|
356
|
+
TelemetryBridge::shared().initialize(
|
|
357
|
+
env == SDKEnvironment::Development ? RAC_ENV_DEVELOPMENT :
|
|
358
|
+
env == SDKEnvironment::Staging ? RAC_ENV_STAGING : RAC_ENV_PRODUCTION,
|
|
359
|
+
persistentDeviceId,
|
|
360
|
+
deviceModel,
|
|
361
|
+
osVersion,
|
|
362
|
+
sdkVersionFromConfig // Use version from config
|
|
363
|
+
);
|
|
364
|
+
|
|
365
|
+
// Register analytics events callback to route events to telemetry
|
|
366
|
+
TelemetryBridge::shared().registerEventsCallback();
|
|
367
|
+
|
|
368
|
+
LOGI("Telemetry initialized with device: %s", persistentDeviceId.c_str());
|
|
369
|
+
} else {
|
|
370
|
+
LOGE("Cannot initialize telemetry: device ID unavailable");
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
LOGI("Core SDK initialized successfully");
|
|
375
|
+
return true;
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
std::shared_ptr<Promise<void>> HybridRunAnywhereCore::destroy() {
|
|
380
|
+
return Promise<void>::async([this]() {
|
|
381
|
+
std::lock_guard<std::mutex> lock(initMutex_);
|
|
382
|
+
|
|
383
|
+
LOGI("Destroying Core SDK...");
|
|
384
|
+
|
|
385
|
+
// Cleanup in reverse order
|
|
386
|
+
TelemetryBridge::shared().shutdown(); // Flush and destroy telemetry first
|
|
387
|
+
EventBridge::shared().unregisterFromEvents();
|
|
388
|
+
DownloadBridge::shared().shutdown();
|
|
389
|
+
StorageBridge::shared().shutdown();
|
|
390
|
+
ModelRegistryBridge::shared().shutdown();
|
|
391
|
+
InitBridge::shared().shutdown();
|
|
392
|
+
|
|
393
|
+
LOGI("Core SDK destroyed");
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::isInitialized() {
|
|
398
|
+
return Promise<bool>::async([]() {
|
|
399
|
+
return InitBridge::shared().isInitialized();
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getBackendInfo() {
|
|
404
|
+
return Promise<std::string>::async([]() {
|
|
405
|
+
// Check if SDK is initialized using the actual InitBridge state
|
|
406
|
+
bool isInitialized = InitBridge::shared().isInitialized();
|
|
407
|
+
|
|
408
|
+
std::string status = isInitialized ? "initialized" : "not_initialized";
|
|
409
|
+
std::string name = isInitialized ? "RunAnywhere Core" : "Not initialized";
|
|
410
|
+
|
|
411
|
+
return buildJsonObject({
|
|
412
|
+
{"name", jsonString(name)},
|
|
413
|
+
{"status", jsonString(status)},
|
|
414
|
+
{"version", jsonString("0.2.0")},
|
|
415
|
+
{"api", jsonString("rac_*")},
|
|
416
|
+
{"source", jsonString("runanywhere-commons")},
|
|
417
|
+
{"module", jsonString("core")},
|
|
418
|
+
{"initialized", isInitialized ? "true" : "false"}
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// ============================================================================
|
|
424
|
+
// Authentication
|
|
425
|
+
// ============================================================================
|
|
426
|
+
|
|
427
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::authenticate(
|
|
428
|
+
const std::string& apiKey) {
|
|
429
|
+
return Promise<bool>::async([this, apiKey]() -> bool {
|
|
430
|
+
LOGI("Authenticating...");
|
|
431
|
+
|
|
432
|
+
// Build auth request JSON
|
|
433
|
+
std::string deviceId = DeviceBridge::shared().getDeviceId();
|
|
434
|
+
// Use actual platform (ios/android) as backend only accepts these values
|
|
435
|
+
#if defined(__APPLE__)
|
|
436
|
+
std::string platform = "ios";
|
|
437
|
+
#elif defined(ANDROID) || defined(__ANDROID__)
|
|
438
|
+
std::string platform = "android";
|
|
439
|
+
#else
|
|
440
|
+
std::string platform = "ios"; // Default to ios for unknown platforms
|
|
441
|
+
#endif
|
|
442
|
+
// Use centralized SDK version from InitBridge (set from TypeScript SDKConstants)
|
|
443
|
+
std::string sdkVersion = InitBridge::shared().getSdkVersion();
|
|
444
|
+
|
|
445
|
+
std::string requestJson = AuthBridge::shared().buildAuthenticateRequestJSON(
|
|
446
|
+
apiKey, deviceId, platform, sdkVersion
|
|
447
|
+
);
|
|
448
|
+
|
|
449
|
+
if (requestJson.empty()) {
|
|
450
|
+
setLastError("Failed to build auth request");
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// NOTE: HTTP request must be made by JS layer
|
|
455
|
+
// This C++ method just prepares the request JSON
|
|
456
|
+
// The JS layer should:
|
|
457
|
+
// 1. Call this method to prepare
|
|
458
|
+
// 2. Make HTTP POST to /api/v1/auth/sdk/authenticate
|
|
459
|
+
// 3. Call handleAuthResponse() with the response
|
|
460
|
+
|
|
461
|
+
// For now, we indicate that auth JSON is prepared
|
|
462
|
+
LOGI("Auth request JSON prepared. HTTP must be done by JS layer.");
|
|
463
|
+
return true;
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::isAuthenticated() {
|
|
468
|
+
return Promise<bool>::async([]() -> bool {
|
|
469
|
+
return AuthBridge::shared().isAuthenticated();
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getUserId() {
|
|
474
|
+
return Promise<std::string>::async([]() -> std::string {
|
|
475
|
+
return AuthBridge::shared().getUserId();
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getOrganizationId() {
|
|
480
|
+
return Promise<std::string>::async([]() -> std::string {
|
|
481
|
+
return AuthBridge::shared().getOrganizationId();
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::setAuthTokens(
|
|
486
|
+
const std::string& authResponseJson) {
|
|
487
|
+
return Promise<bool>::async([this, authResponseJson]() -> bool {
|
|
488
|
+
LOGI("Setting auth tokens from JS authentication response...");
|
|
489
|
+
|
|
490
|
+
// Parse the auth response
|
|
491
|
+
AuthResponse response = AuthBridge::shared().handleAuthResponse(authResponseJson);
|
|
492
|
+
|
|
493
|
+
if (response.success) {
|
|
494
|
+
// IMPORTANT: Actually store the tokens in AuthBridge!
|
|
495
|
+
// handleAuthResponse only parses, setAuth stores them
|
|
496
|
+
AuthBridge::shared().setAuth(response);
|
|
497
|
+
|
|
498
|
+
LOGI("Auth tokens set successfully. Token expires in %lld seconds",
|
|
499
|
+
static_cast<long long>(response.expiresIn));
|
|
500
|
+
LOGD("Access token stored (length=%zu)", response.accessToken.length());
|
|
501
|
+
return true;
|
|
502
|
+
} else {
|
|
503
|
+
LOGE("Failed to set auth tokens: %s", response.error.c_str());
|
|
504
|
+
setLastError("Failed to set auth tokens: " + response.error);
|
|
505
|
+
return false;
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// ============================================================================
|
|
511
|
+
// Device Registration
|
|
512
|
+
// ============================================================================
|
|
513
|
+
|
|
514
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::registerDevice(
|
|
515
|
+
const std::string& environmentJson) {
|
|
516
|
+
return Promise<bool>::async([this, environmentJson]() -> bool {
|
|
517
|
+
LOGI("Registering device...");
|
|
518
|
+
|
|
519
|
+
// Parse environment
|
|
520
|
+
std::string envStr = extractStringValue(environmentJson, "environment", "production");
|
|
521
|
+
rac_environment_t env = RAC_ENV_PRODUCTION;
|
|
522
|
+
if (envStr == "development") env = RAC_ENV_DEVELOPMENT;
|
|
523
|
+
else if (envStr == "staging") env = RAC_ENV_STAGING;
|
|
524
|
+
|
|
525
|
+
std::string buildToken = extractStringValue(environmentJson, "buildToken", "");
|
|
526
|
+
std::string supabaseKey = extractStringValue(environmentJson, "supabaseKey", "");
|
|
527
|
+
|
|
528
|
+
// For development mode, get build token from C++ dev config if not provided
|
|
529
|
+
// This matches Swift's CppBridge.DevConfig.buildToken behavior
|
|
530
|
+
if (buildToken.empty() && env == RAC_ENV_DEVELOPMENT) {
|
|
531
|
+
const char* devBuildToken = rac_dev_config_get_build_token();
|
|
532
|
+
if (devBuildToken && strlen(devBuildToken) > 0) {
|
|
533
|
+
buildToken = devBuildToken;
|
|
534
|
+
LOGD("Using build token from dev config");
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Set up platform callbacks (matches Swift's CppBridge.Device.registerCallbacks)
|
|
539
|
+
DevicePlatformCallbacks callbacks;
|
|
540
|
+
|
|
541
|
+
// Device info callback - populates all fields needed by backend
|
|
542
|
+
// Matches Swift's CppBridge+Device.swift get_device_info callback
|
|
543
|
+
callbacks.getDeviceInfo = []() -> DeviceInfo {
|
|
544
|
+
DeviceInfo info;
|
|
545
|
+
|
|
546
|
+
// Core identification
|
|
547
|
+
info.deviceId = InitBridge::shared().getPersistentDeviceUUID();
|
|
548
|
+
// Use actual platform (ios/android) as backend only accepts these values
|
|
549
|
+
#if defined(__APPLE__)
|
|
550
|
+
info.platform = "ios";
|
|
551
|
+
#elif defined(ANDROID) || defined(__ANDROID__)
|
|
552
|
+
info.platform = "android";
|
|
553
|
+
#else
|
|
554
|
+
info.platform = "ios"; // Default to ios for unknown platforms
|
|
555
|
+
#endif
|
|
556
|
+
// Use centralized SDK version from InitBridge (set from TypeScript SDKConstants)
|
|
557
|
+
info.sdkVersion = InitBridge::shared().getSdkVersion();
|
|
558
|
+
|
|
559
|
+
// Device hardware info from platform-specific code
|
|
560
|
+
info.deviceModel = InitBridge::shared().getDeviceModel();
|
|
561
|
+
info.deviceName = info.deviceModel; // Use model as name (React Native doesn't expose device name)
|
|
562
|
+
info.osVersion = InitBridge::shared().getOSVersion();
|
|
563
|
+
info.chipName = InitBridge::shared().getChipName();
|
|
564
|
+
info.architecture = InitBridge::shared().getArchitecture();
|
|
565
|
+
info.totalMemory = InitBridge::shared().getTotalMemory();
|
|
566
|
+
info.availableMemory = InitBridge::shared().getAvailableMemory();
|
|
567
|
+
info.coreCount = InitBridge::shared().getCoreCount();
|
|
568
|
+
|
|
569
|
+
// Form factor detection (matches Swift SDK: device.userInterfaceIdiom == .pad)
|
|
570
|
+
// Uses platform-specific detection via InitBridge::isTablet()
|
|
571
|
+
bool isTabletDevice = InitBridge::shared().isTablet();
|
|
572
|
+
info.formFactor = isTabletDevice ? "tablet" : "phone";
|
|
573
|
+
|
|
574
|
+
// Platform-specific values
|
|
575
|
+
#if defined(__APPLE__)
|
|
576
|
+
info.osName = "iOS";
|
|
577
|
+
info.gpuFamily = InitBridge::shared().getGPUFamily(); // "apple"
|
|
578
|
+
info.hasNeuralEngine = true;
|
|
579
|
+
info.neuralEngineCores = 16; // Modern iPhones have 16 ANE cores
|
|
580
|
+
#elif defined(ANDROID) || defined(__ANDROID__)
|
|
581
|
+
info.osName = "Android";
|
|
582
|
+
info.gpuFamily = InitBridge::shared().getGPUFamily(); // "mali", "adreno", etc.
|
|
583
|
+
info.hasNeuralEngine = false;
|
|
584
|
+
info.neuralEngineCores = 0;
|
|
585
|
+
#else
|
|
586
|
+
info.osName = "Unknown";
|
|
587
|
+
info.gpuFamily = "unknown";
|
|
588
|
+
info.hasNeuralEngine = false;
|
|
589
|
+
info.neuralEngineCores = 0;
|
|
590
|
+
#endif
|
|
591
|
+
|
|
592
|
+
// Battery info (not available in React Native easily, use defaults)
|
|
593
|
+
info.batteryLevel = -1.0; // Unknown
|
|
594
|
+
info.batteryState = ""; // Unknown
|
|
595
|
+
info.isLowPowerMode = false;
|
|
596
|
+
|
|
597
|
+
// Core distribution (approximate for mobile devices)
|
|
598
|
+
info.performanceCores = info.coreCount > 4 ? 2 : 1;
|
|
599
|
+
info.efficiencyCores = info.coreCount - info.performanceCores;
|
|
600
|
+
|
|
601
|
+
return info;
|
|
602
|
+
};
|
|
603
|
+
|
|
604
|
+
// Device ID callback
|
|
605
|
+
callbacks.getDeviceId = []() -> std::string {
|
|
606
|
+
return InitBridge::shared().getPersistentDeviceUUID();
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
// Check registration status callback
|
|
610
|
+
callbacks.isRegistered = []() -> bool {
|
|
611
|
+
// Check UserDefaults/SharedPrefs for registration status
|
|
612
|
+
std::string value;
|
|
613
|
+
if (InitBridge::shared().secureGet("com.runanywhere.sdk.deviceRegistered", value)) {
|
|
614
|
+
return value == "true";
|
|
615
|
+
}
|
|
616
|
+
return false;
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
// Set registration status callback
|
|
620
|
+
callbacks.setRegistered = [](bool registered) {
|
|
621
|
+
InitBridge::shared().secureSet("com.runanywhere.sdk.deviceRegistered",
|
|
622
|
+
registered ? "true" : "false");
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
// HTTP POST callback - key for device registration!
|
|
626
|
+
// Uses native URLSession (iOS) or HttpURLConnection (Android)
|
|
627
|
+
// All credentials come from C++ dev config (matches Swift's CppBridge.DevConfig)
|
|
628
|
+
callbacks.httpPost = [env](
|
|
629
|
+
const std::string& endpoint,
|
|
630
|
+
const std::string& jsonBody,
|
|
631
|
+
bool requiresAuth
|
|
632
|
+
) -> std::tuple<bool, int, std::string, std::string> {
|
|
633
|
+
// Build full URL based on environment (matches Swift HTTPService)
|
|
634
|
+
std::string baseURL;
|
|
635
|
+
std::string apiKey;
|
|
636
|
+
|
|
637
|
+
if (env == RAC_ENV_DEVELOPMENT) {
|
|
638
|
+
// Development: Use Supabase from C++ dev config
|
|
639
|
+
// This matches Swift's CppBridge.DevConfig.supabaseURL/supabaseKey
|
|
640
|
+
const char* devUrl = rac_dev_config_get_supabase_url();
|
|
641
|
+
const char* devKey = rac_dev_config_get_supabase_key();
|
|
642
|
+
|
|
643
|
+
baseURL = devUrl ? devUrl : "https://fhtgjtxuoikwwouxqzrn.supabase.co";
|
|
644
|
+
apiKey = devKey ? devKey : "";
|
|
645
|
+
|
|
646
|
+
LOGD("Using Supabase from dev config: %s", baseURL.c_str());
|
|
647
|
+
} else {
|
|
648
|
+
// Production/Staging: Use configured Railway URL
|
|
649
|
+
// These come from SDK initialization (App.tsx -> RunAnywhere.initialize)
|
|
650
|
+
baseURL = InitBridge::shared().getBaseURL();
|
|
651
|
+
|
|
652
|
+
// For production mode, prefer JWT access token (from authentication)
|
|
653
|
+
// over raw API key. This matches Swift/Kotlin behavior.
|
|
654
|
+
std::string accessToken = AuthBridge::shared().getAccessToken();
|
|
655
|
+
if (!accessToken.empty()) {
|
|
656
|
+
apiKey = accessToken; // Use JWT for Authorization header
|
|
657
|
+
LOGD("Using JWT access token for device registration");
|
|
658
|
+
} else {
|
|
659
|
+
// Fallback to API key if not authenticated yet
|
|
660
|
+
apiKey = InitBridge::shared().getApiKey();
|
|
661
|
+
LOGD("Using API key for device registration (not authenticated)");
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
// Fallback to default if not configured
|
|
665
|
+
if (baseURL.empty()) {
|
|
666
|
+
baseURL = "https://api.runanywhere.ai";
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
LOGD("Using production config: %s", baseURL.c_str());
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
std::string fullURL = baseURL + endpoint;
|
|
673
|
+
LOGI("Device HTTP POST to: %s (env=%d)", fullURL.c_str(), env);
|
|
674
|
+
|
|
675
|
+
return InitBridge::shared().httpPostSync(fullURL, jsonBody, apiKey);
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
// Set callbacks on DeviceBridge
|
|
679
|
+
DeviceBridge::shared().setPlatformCallbacks(callbacks);
|
|
680
|
+
|
|
681
|
+
// Register callbacks with C++
|
|
682
|
+
rac_result_t result = DeviceBridge::shared().registerCallbacks();
|
|
683
|
+
if (result != RAC_SUCCESS) {
|
|
684
|
+
setLastError("Failed to register device callbacks: " + std::to_string(result));
|
|
685
|
+
return false;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// Now register device
|
|
689
|
+
result = DeviceBridge::shared().registerIfNeeded(env, buildToken);
|
|
690
|
+
if (result != RAC_SUCCESS) {
|
|
691
|
+
setLastError("Device registration failed: " + std::to_string(result));
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
LOGI("Device registered successfully");
|
|
696
|
+
return true;
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::isDeviceRegistered() {
|
|
701
|
+
return Promise<bool>::async([]() -> bool {
|
|
702
|
+
return DeviceBridge::shared().isRegistered();
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::clearDeviceRegistration() {
|
|
707
|
+
return Promise<bool>::async([]() -> bool {
|
|
708
|
+
LOGI("Clearing device registration flag for testing...");
|
|
709
|
+
bool success = InitBridge::shared().secureDelete("com.runanywhere.sdk.deviceRegistered");
|
|
710
|
+
if (success) {
|
|
711
|
+
LOGI("Device registration flag cleared successfully");
|
|
712
|
+
} else {
|
|
713
|
+
LOGI("Device registration flag not found (may not exist)");
|
|
714
|
+
}
|
|
715
|
+
return true; // Return true even if key didn't exist
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getDeviceId() {
|
|
720
|
+
return Promise<std::string>::async([]() -> std::string {
|
|
721
|
+
return DeviceBridge::shared().getDeviceId();
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
// ============================================================================
|
|
726
|
+
// Model Registry
|
|
727
|
+
// ============================================================================
|
|
728
|
+
|
|
729
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getAvailableModels() {
|
|
730
|
+
return Promise<std::string>::async([]() -> std::string {
|
|
731
|
+
auto models = ModelRegistryBridge::shared().getAllModels();
|
|
732
|
+
|
|
733
|
+
LOGI("getAvailableModels: Building JSON for %zu models", models.size());
|
|
734
|
+
|
|
735
|
+
std::string result = "[";
|
|
736
|
+
for (size_t i = 0; i < models.size(); i++) {
|
|
737
|
+
if (i > 0) result += ",";
|
|
738
|
+
const auto& m = models[i];
|
|
739
|
+
// Convert C++ enum values to TypeScript string values for compatibility
|
|
740
|
+
std::string categoryStr = "unknown";
|
|
741
|
+
switch (m.category) {
|
|
742
|
+
case RAC_MODEL_CATEGORY_LANGUAGE: categoryStr = "language"; break;
|
|
743
|
+
case RAC_MODEL_CATEGORY_SPEECH_RECOGNITION: categoryStr = "speech-recognition"; break;
|
|
744
|
+
case RAC_MODEL_CATEGORY_SPEECH_SYNTHESIS: categoryStr = "speech-synthesis"; break;
|
|
745
|
+
case RAC_MODEL_CATEGORY_VISION: categoryStr = "vision"; break;
|
|
746
|
+
case RAC_MODEL_CATEGORY_AUDIO: categoryStr = "audio"; break;
|
|
747
|
+
case RAC_MODEL_CATEGORY_MULTIMODAL: categoryStr = "multimodal"; break;
|
|
748
|
+
default: categoryStr = "unknown"; break;
|
|
749
|
+
}
|
|
750
|
+
std::string formatStr = "unknown";
|
|
751
|
+
switch (m.format) {
|
|
752
|
+
case RAC_MODEL_FORMAT_GGUF: formatStr = "gguf"; break;
|
|
753
|
+
case RAC_MODEL_FORMAT_ONNX: formatStr = "onnx"; break;
|
|
754
|
+
case RAC_MODEL_FORMAT_ORT: formatStr = "ort"; break;
|
|
755
|
+
case RAC_MODEL_FORMAT_BIN: formatStr = "bin"; break;
|
|
756
|
+
default: formatStr = "unknown"; break;
|
|
757
|
+
}
|
|
758
|
+
std::string frameworkStr = "unknown";
|
|
759
|
+
switch (m.framework) {
|
|
760
|
+
case RAC_FRAMEWORK_LLAMACPP: frameworkStr = "LlamaCpp"; break;
|
|
761
|
+
case RAC_FRAMEWORK_ONNX: frameworkStr = "ONNX"; break;
|
|
762
|
+
case RAC_FRAMEWORK_FOUNDATION_MODELS: frameworkStr = "FoundationModels"; break;
|
|
763
|
+
case RAC_FRAMEWORK_SYSTEM_TTS: frameworkStr = "SystemTTS"; break;
|
|
764
|
+
default: frameworkStr = "unknown"; break;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
result += buildJsonObject({
|
|
768
|
+
{"id", jsonString(m.id)},
|
|
769
|
+
{"name", jsonString(m.name)},
|
|
770
|
+
{"localPath", jsonString(m.localPath)},
|
|
771
|
+
{"downloadURL", jsonString(m.downloadUrl)}, // TypeScript uses capital U
|
|
772
|
+
{"category", jsonString(categoryStr)}, // String for TypeScript
|
|
773
|
+
{"format", jsonString(formatStr)}, // String for TypeScript
|
|
774
|
+
{"preferredFramework", jsonString(frameworkStr)}, // String for TypeScript
|
|
775
|
+
{"downloadSize", std::to_string(m.downloadSize)},
|
|
776
|
+
{"memoryRequired", std::to_string(m.memoryRequired)},
|
|
777
|
+
{"supportsThinking", m.supportsThinking ? "true" : "false"},
|
|
778
|
+
{"isDownloaded", m.isDownloaded ? "true" : "false"},
|
|
779
|
+
{"isAvailable", "true"} // Models in registry are available
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
result += "]";
|
|
783
|
+
|
|
784
|
+
LOGD("getAvailableModels: JSON length=%zu", result.length());
|
|
785
|
+
|
|
786
|
+
return result;
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getModelInfo(
|
|
791
|
+
const std::string& modelId) {
|
|
792
|
+
return Promise<std::string>::async([modelId]() -> std::string {
|
|
793
|
+
auto model = ModelRegistryBridge::shared().getModel(modelId);
|
|
794
|
+
if (!model.has_value()) {
|
|
795
|
+
return "{}";
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const auto& m = model.value();
|
|
799
|
+
|
|
800
|
+
// Convert enums to strings (same as getAvailableModels)
|
|
801
|
+
std::string categoryStr = "unknown";
|
|
802
|
+
switch (m.category) {
|
|
803
|
+
case RAC_MODEL_CATEGORY_LANGUAGE: categoryStr = "language"; break;
|
|
804
|
+
case RAC_MODEL_CATEGORY_SPEECH_RECOGNITION: categoryStr = "speech-recognition"; break;
|
|
805
|
+
case RAC_MODEL_CATEGORY_SPEECH_SYNTHESIS: categoryStr = "speech-synthesis"; break;
|
|
806
|
+
case RAC_MODEL_CATEGORY_AUDIO: categoryStr = "audio"; break;
|
|
807
|
+
case RAC_MODEL_CATEGORY_VISION: categoryStr = "vision"; break;
|
|
808
|
+
case RAC_MODEL_CATEGORY_IMAGE_GENERATION: categoryStr = "image-generation"; break;
|
|
809
|
+
case RAC_MODEL_CATEGORY_MULTIMODAL: categoryStr = "multimodal"; break;
|
|
810
|
+
default: categoryStr = "unknown"; break;
|
|
811
|
+
}
|
|
812
|
+
std::string formatStr = "unknown";
|
|
813
|
+
switch (m.format) {
|
|
814
|
+
case RAC_MODEL_FORMAT_GGUF: formatStr = "gguf"; break;
|
|
815
|
+
case RAC_MODEL_FORMAT_ONNX: formatStr = "onnx"; break;
|
|
816
|
+
case RAC_MODEL_FORMAT_ORT: formatStr = "ort"; break;
|
|
817
|
+
case RAC_MODEL_FORMAT_BIN: formatStr = "bin"; break;
|
|
818
|
+
default: formatStr = "unknown"; break;
|
|
819
|
+
}
|
|
820
|
+
std::string frameworkStr = "unknown";
|
|
821
|
+
switch (m.framework) {
|
|
822
|
+
case RAC_FRAMEWORK_LLAMACPP: frameworkStr = "LlamaCpp"; break;
|
|
823
|
+
case RAC_FRAMEWORK_ONNX: frameworkStr = "ONNX"; break;
|
|
824
|
+
case RAC_FRAMEWORK_FOUNDATION_MODELS: frameworkStr = "FoundationModels"; break;
|
|
825
|
+
case RAC_FRAMEWORK_SYSTEM_TTS: frameworkStr = "SystemTTS"; break;
|
|
826
|
+
default: frameworkStr = "unknown"; break;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
return buildJsonObject({
|
|
830
|
+
{"id", jsonString(m.id)},
|
|
831
|
+
{"name", jsonString(m.name)},
|
|
832
|
+
{"description", jsonString(m.description)},
|
|
833
|
+
{"localPath", jsonString(m.localPath)},
|
|
834
|
+
{"downloadURL", jsonString(m.downloadUrl)}, // Fixed: downloadURL (capital URL) to match TypeScript
|
|
835
|
+
{"category", jsonString(categoryStr)}, // String for TypeScript
|
|
836
|
+
{"format", jsonString(formatStr)}, // String for TypeScript
|
|
837
|
+
{"preferredFramework", jsonString(frameworkStr)}, // String for TypeScript (preferredFramework key)
|
|
838
|
+
{"downloadSize", std::to_string(m.downloadSize)},
|
|
839
|
+
{"memoryRequired", std::to_string(m.memoryRequired)},
|
|
840
|
+
{"contextLength", std::to_string(m.contextLength)},
|
|
841
|
+
{"supportsThinking", m.supportsThinking ? "true" : "false"},
|
|
842
|
+
{"isDownloaded", m.isDownloaded ? "true" : "false"},
|
|
843
|
+
{"isAvailable", "true"} // Added isAvailable field
|
|
844
|
+
});
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::isModelDownloaded(
|
|
849
|
+
const std::string& modelId) {
|
|
850
|
+
return Promise<bool>::async([modelId]() -> bool {
|
|
851
|
+
return ModelRegistryBridge::shared().isModelDownloaded(modelId);
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getModelPath(
|
|
856
|
+
const std::string& modelId) {
|
|
857
|
+
return Promise<std::string>::async([modelId]() -> std::string {
|
|
858
|
+
auto path = ModelRegistryBridge::shared().getModelPath(modelId);
|
|
859
|
+
return path.value_or("");
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::registerModel(
|
|
864
|
+
const std::string& modelJson) {
|
|
865
|
+
return Promise<bool>::async([modelJson]() -> bool {
|
|
866
|
+
LOGI("Registering model from JSON: %.200s", modelJson.c_str());
|
|
867
|
+
|
|
868
|
+
ModelInfo model;
|
|
869
|
+
model.id = extractStringValue(modelJson, "id");
|
|
870
|
+
model.name = extractStringValue(modelJson, "name");
|
|
871
|
+
model.description = extractStringValue(modelJson, "description");
|
|
872
|
+
model.localPath = extractStringValue(modelJson, "localPath");
|
|
873
|
+
|
|
874
|
+
// Support both TypeScript naming (downloadURL) and C++ naming (downloadUrl)
|
|
875
|
+
model.downloadUrl = extractStringValue(modelJson, "downloadURL");
|
|
876
|
+
if (model.downloadUrl.empty()) {
|
|
877
|
+
model.downloadUrl = extractStringValue(modelJson, "downloadUrl");
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
model.downloadSize = extractIntValue(modelJson, "downloadSize", 0);
|
|
881
|
+
model.memoryRequired = extractIntValue(modelJson, "memoryRequired", 0);
|
|
882
|
+
model.contextLength = extractIntValue(modelJson, "contextLength", 0);
|
|
883
|
+
model.supportsThinking = extractBoolValue(modelJson, "supportsThinking", false);
|
|
884
|
+
|
|
885
|
+
// Handle category - could be string (TypeScript) or int
|
|
886
|
+
std::string categoryStr = extractStringValue(modelJson, "category");
|
|
887
|
+
if (!categoryStr.empty()) {
|
|
888
|
+
model.category = categoryFromString(categoryStr);
|
|
889
|
+
} else {
|
|
890
|
+
model.category = static_cast<rac_model_category_t>(extractIntValue(modelJson, "category", RAC_MODEL_CATEGORY_UNKNOWN));
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
// Handle format - could be string (TypeScript) or int
|
|
894
|
+
std::string formatStr = extractStringValue(modelJson, "format");
|
|
895
|
+
if (!formatStr.empty()) {
|
|
896
|
+
model.format = formatFromString(formatStr);
|
|
897
|
+
} else {
|
|
898
|
+
model.format = static_cast<rac_model_format_t>(extractIntValue(modelJson, "format", RAC_MODEL_FORMAT_UNKNOWN));
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// Handle framework - prefer string extraction for TypeScript compatibility
|
|
902
|
+
std::string frameworkStr = extractStringValue(modelJson, "preferredFramework");
|
|
903
|
+
if (!frameworkStr.empty()) {
|
|
904
|
+
model.framework = frameworkFromString(frameworkStr);
|
|
905
|
+
} else {
|
|
906
|
+
frameworkStr = extractStringValue(modelJson, "framework");
|
|
907
|
+
if (!frameworkStr.empty()) {
|
|
908
|
+
model.framework = frameworkFromString(frameworkStr);
|
|
909
|
+
} else {
|
|
910
|
+
model.framework = static_cast<rac_inference_framework_t>(extractIntValue(modelJson, "preferredFramework", RAC_FRAMEWORK_UNKNOWN));
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
LOGI("Registering model: id=%s, name=%s, framework=%d, category=%d",
|
|
915
|
+
model.id.c_str(), model.name.c_str(), model.framework, model.category);
|
|
916
|
+
|
|
917
|
+
rac_result_t result = ModelRegistryBridge::shared().addModel(model);
|
|
918
|
+
|
|
919
|
+
if (result == RAC_SUCCESS) {
|
|
920
|
+
LOGI("✅ Model registered successfully: %s", model.id.c_str());
|
|
921
|
+
} else {
|
|
922
|
+
LOGE("❌ Model registration failed: %s, result=%d", model.id.c_str(), result);
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
return result == RAC_SUCCESS;
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// ============================================================================
|
|
930
|
+
// Download Service
|
|
931
|
+
// ============================================================================
|
|
932
|
+
|
|
933
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::downloadModel(
|
|
934
|
+
const std::string& modelId,
|
|
935
|
+
const std::string& url,
|
|
936
|
+
const std::string& destPath) {
|
|
937
|
+
return Promise<bool>::async([this, modelId, url, destPath]() -> bool {
|
|
938
|
+
LOGI("Starting download: %s", modelId.c_str());
|
|
939
|
+
|
|
940
|
+
std::string taskId = DownloadBridge::shared().startDownload(
|
|
941
|
+
modelId, url, destPath, false, // requiresExtraction
|
|
942
|
+
[](const DownloadProgress& progress) {
|
|
943
|
+
LOGD("Download progress: %.1f%%", progress.overallProgress * 100);
|
|
944
|
+
}
|
|
945
|
+
);
|
|
946
|
+
|
|
947
|
+
if (taskId.empty()) {
|
|
948
|
+
setLastError("Failed to start download");
|
|
949
|
+
return false;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
return true;
|
|
953
|
+
});
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::cancelDownload(
|
|
957
|
+
const std::string& taskId) {
|
|
958
|
+
return Promise<bool>::async([taskId]() -> bool {
|
|
959
|
+
rac_result_t result = DownloadBridge::shared().cancelDownload(taskId);
|
|
960
|
+
return result == RAC_SUCCESS;
|
|
961
|
+
});
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getDownloadProgress(
|
|
965
|
+
const std::string& taskId) {
|
|
966
|
+
return Promise<std::string>::async([taskId]() -> std::string {
|
|
967
|
+
auto progress = DownloadBridge::shared().getProgress(taskId);
|
|
968
|
+
if (!progress.has_value()) {
|
|
969
|
+
return "{}";
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
const auto& p = progress.value();
|
|
973
|
+
std::string stateStr;
|
|
974
|
+
switch (p.state) {
|
|
975
|
+
case DownloadState::Pending: stateStr = "pending"; break;
|
|
976
|
+
case DownloadState::Downloading: stateStr = "downloading"; break;
|
|
977
|
+
case DownloadState::Extracting: stateStr = "extracting"; break;
|
|
978
|
+
case DownloadState::Retrying: stateStr = "retrying"; break;
|
|
979
|
+
case DownloadState::Completed: stateStr = "completed"; break;
|
|
980
|
+
case DownloadState::Failed: stateStr = "failed"; break;
|
|
981
|
+
case DownloadState::Cancelled: stateStr = "cancelled"; break;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
return buildJsonObject({
|
|
985
|
+
{"bytesDownloaded", std::to_string(p.bytesDownloaded)},
|
|
986
|
+
{"totalBytes", std::to_string(p.totalBytes)},
|
|
987
|
+
{"overallProgress", std::to_string(p.overallProgress)},
|
|
988
|
+
{"stageProgress", std::to_string(p.stageProgress)},
|
|
989
|
+
{"state", jsonString(stateStr)},
|
|
990
|
+
{"speed", std::to_string(p.speed)},
|
|
991
|
+
{"estimatedTimeRemaining", std::to_string(p.estimatedTimeRemaining)},
|
|
992
|
+
{"retryAttempt", std::to_string(p.retryAttempt)},
|
|
993
|
+
{"errorCode", std::to_string(p.errorCode)},
|
|
994
|
+
{"errorMessage", jsonString(p.errorMessage)}
|
|
995
|
+
});
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// ============================================================================
|
|
1000
|
+
// Storage
|
|
1001
|
+
// ============================================================================
|
|
1002
|
+
|
|
1003
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getStorageInfo() {
|
|
1004
|
+
return Promise<std::string>::async([]() {
|
|
1005
|
+
auto registryHandle = ModelRegistryBridge::shared().getHandle();
|
|
1006
|
+
auto info = StorageBridge::shared().analyzeStorage(registryHandle);
|
|
1007
|
+
|
|
1008
|
+
return buildJsonObject({
|
|
1009
|
+
{"totalDeviceSpace", std::to_string(info.deviceStorage.totalSpace)},
|
|
1010
|
+
{"freeDeviceSpace", std::to_string(info.deviceStorage.freeSpace)},
|
|
1011
|
+
{"usedDeviceSpace", std::to_string(info.deviceStorage.usedSpace)},
|
|
1012
|
+
{"documentsSize", std::to_string(info.appStorage.documentsSize)},
|
|
1013
|
+
{"cacheSize", std::to_string(info.appStorage.cacheSize)},
|
|
1014
|
+
{"appSupportSize", std::to_string(info.appStorage.appSupportSize)},
|
|
1015
|
+
{"totalAppSize", std::to_string(info.appStorage.totalSize)},
|
|
1016
|
+
{"totalModelsSize", std::to_string(info.totalModelsSize)},
|
|
1017
|
+
{"modelCount", std::to_string(info.models.size())}
|
|
1018
|
+
});
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::clearCache() {
|
|
1023
|
+
return Promise<bool>::async([]() {
|
|
1024
|
+
LOGI("Clearing cache...");
|
|
1025
|
+
|
|
1026
|
+
// Clear the model assignment cache (in-memory cache for model assignments)
|
|
1027
|
+
rac_model_assignment_clear_cache();
|
|
1028
|
+
|
|
1029
|
+
LOGI("Cache cleared successfully");
|
|
1030
|
+
return true;
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::deleteModel(
|
|
1035
|
+
const std::string& modelId) {
|
|
1036
|
+
return Promise<bool>::async([modelId]() {
|
|
1037
|
+
LOGI("Deleting model: %s", modelId.c_str());
|
|
1038
|
+
rac_result_t result = ModelRegistryBridge::shared().removeModel(modelId);
|
|
1039
|
+
return result == RAC_SUCCESS;
|
|
1040
|
+
});
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// ============================================================================
|
|
1044
|
+
// Events
|
|
1045
|
+
// ============================================================================
|
|
1046
|
+
|
|
1047
|
+
std::shared_ptr<Promise<void>> HybridRunAnywhereCore::emitEvent(
|
|
1048
|
+
const std::string& eventJson) {
|
|
1049
|
+
return Promise<void>::async([eventJson]() -> void {
|
|
1050
|
+
std::string type = extractStringValue(eventJson, "type");
|
|
1051
|
+
std::string categoryStr = extractStringValue(eventJson, "category", "sdk");
|
|
1052
|
+
|
|
1053
|
+
EventCategory category = EventCategory::SDK;
|
|
1054
|
+
if (categoryStr == "model") category = EventCategory::Model;
|
|
1055
|
+
else if (categoryStr == "llm") category = EventCategory::LLM;
|
|
1056
|
+
else if (categoryStr == "stt") category = EventCategory::STT;
|
|
1057
|
+
else if (categoryStr == "tts") category = EventCategory::TTS;
|
|
1058
|
+
|
|
1059
|
+
EventBridge::shared().trackEvent(type, category, EventDestination::All, eventJson);
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::pollEvents() {
|
|
1064
|
+
// Events are push-based via callback, not polling
|
|
1065
|
+
return Promise<std::string>::async([]() -> std::string {
|
|
1066
|
+
return "[]";
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
// ============================================================================
|
|
1071
|
+
// HTTP Client
|
|
1072
|
+
// ============================================================================
|
|
1073
|
+
|
|
1074
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::configureHttp(
|
|
1075
|
+
const std::string& baseUrl,
|
|
1076
|
+
const std::string& apiKey) {
|
|
1077
|
+
return Promise<bool>::async([baseUrl, apiKey]() -> bool {
|
|
1078
|
+
HTTPBridge::shared().configure(baseUrl, apiKey);
|
|
1079
|
+
return HTTPBridge::shared().isConfigured();
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::httpPost(
|
|
1084
|
+
const std::string& path,
|
|
1085
|
+
const std::string& bodyJson) {
|
|
1086
|
+
return Promise<std::string>::async([this, path, bodyJson]() -> std::string {
|
|
1087
|
+
// HTTP is handled by JS layer
|
|
1088
|
+
// This returns URL for JS to use
|
|
1089
|
+
std::string url = HTTPBridge::shared().buildURL(path);
|
|
1090
|
+
|
|
1091
|
+
// Try to use registered executor if available
|
|
1092
|
+
auto response = HTTPBridge::shared().execute("POST", path, bodyJson, true);
|
|
1093
|
+
if (response.has_value()) {
|
|
1094
|
+
if (response->success) {
|
|
1095
|
+
return response->body;
|
|
1096
|
+
} else {
|
|
1097
|
+
throw std::runtime_error(response->error);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// No executor - return error indicating HTTP must be done by JS
|
|
1102
|
+
throw std::runtime_error("HTTP executor not registered. Use JS layer for HTTP requests.");
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::httpGet(
|
|
1107
|
+
const std::string& path) {
|
|
1108
|
+
return Promise<std::string>::async([this, path]() -> std::string {
|
|
1109
|
+
auto response = HTTPBridge::shared().execute("GET", path, "", true);
|
|
1110
|
+
if (response.has_value()) {
|
|
1111
|
+
if (response->success) {
|
|
1112
|
+
return response->body;
|
|
1113
|
+
} else {
|
|
1114
|
+
throw std::runtime_error(response->error);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
throw std::runtime_error("HTTP executor not registered. Use JS layer for HTTP requests.");
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
// ============================================================================
|
|
1123
|
+
// Utility Functions
|
|
1124
|
+
// ============================================================================
|
|
1125
|
+
|
|
1126
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getLastError() {
|
|
1127
|
+
return Promise<std::string>::async([this]() { return lastError_; });
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
// Forward declaration for platform-specific archive extraction
|
|
1131
|
+
#if defined(__APPLE__)
|
|
1132
|
+
extern "C" bool ArchiveUtility_extract(const char* archivePath, const char* destinationPath);
|
|
1133
|
+
#elif defined(__ANDROID__)
|
|
1134
|
+
// On Android, we'll call the Kotlin ArchiveUtility via JNI in a separate helper
|
|
1135
|
+
extern "C" bool ArchiveUtility_extractAndroid(const char* archivePath, const char* destinationPath);
|
|
1136
|
+
#endif
|
|
1137
|
+
|
|
1138
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::extractArchive(
|
|
1139
|
+
const std::string& archivePath,
|
|
1140
|
+
const std::string& destPath) {
|
|
1141
|
+
return Promise<bool>::async([this, archivePath, destPath]() {
|
|
1142
|
+
LOGI("extractArchive: %s -> %s", archivePath.c_str(), destPath.c_str());
|
|
1143
|
+
|
|
1144
|
+
#if defined(__APPLE__)
|
|
1145
|
+
// iOS: Call Swift ArchiveUtility
|
|
1146
|
+
bool success = ArchiveUtility_extract(archivePath.c_str(), destPath.c_str());
|
|
1147
|
+
if (success) {
|
|
1148
|
+
LOGI("iOS archive extraction succeeded");
|
|
1149
|
+
return true;
|
|
1150
|
+
} else {
|
|
1151
|
+
LOGE("iOS archive extraction failed");
|
|
1152
|
+
setLastError("Archive extraction failed");
|
|
1153
|
+
return false;
|
|
1154
|
+
}
|
|
1155
|
+
#elif defined(__ANDROID__)
|
|
1156
|
+
// Android: Call Kotlin ArchiveUtility via JNI
|
|
1157
|
+
bool success = ArchiveUtility_extractAndroid(archivePath.c_str(), destPath.c_str());
|
|
1158
|
+
if (success) {
|
|
1159
|
+
LOGI("Android archive extraction succeeded");
|
|
1160
|
+
return true;
|
|
1161
|
+
} else {
|
|
1162
|
+
LOGE("Android archive extraction failed");
|
|
1163
|
+
setLastError("Archive extraction failed");
|
|
1164
|
+
return false;
|
|
1165
|
+
}
|
|
1166
|
+
#else
|
|
1167
|
+
LOGW("Archive extraction not supported on this platform");
|
|
1168
|
+
setLastError("Archive extraction not supported");
|
|
1169
|
+
return false;
|
|
1170
|
+
#endif
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getDeviceCapabilities() {
|
|
1175
|
+
return Promise<std::string>::async([]() {
|
|
1176
|
+
std::string platform =
|
|
1177
|
+
#if defined(__APPLE__)
|
|
1178
|
+
"ios";
|
|
1179
|
+
#else
|
|
1180
|
+
"android";
|
|
1181
|
+
#endif
|
|
1182
|
+
bool supportsMetal =
|
|
1183
|
+
#if defined(__APPLE__)
|
|
1184
|
+
true;
|
|
1185
|
+
#else
|
|
1186
|
+
false;
|
|
1187
|
+
#endif
|
|
1188
|
+
bool supportsVulkan =
|
|
1189
|
+
#if defined(__APPLE__)
|
|
1190
|
+
false;
|
|
1191
|
+
#else
|
|
1192
|
+
true;
|
|
1193
|
+
#endif
|
|
1194
|
+
return buildJsonObject({
|
|
1195
|
+
{"platform", jsonString(platform)},
|
|
1196
|
+
{"supports_metal", supportsMetal ? "true" : "false"},
|
|
1197
|
+
{"supports_vulkan", supportsVulkan ? "true" : "false"},
|
|
1198
|
+
{"api", jsonString("rac_*")},
|
|
1199
|
+
{"module", jsonString("core")}
|
|
1200
|
+
});
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
std::shared_ptr<Promise<double>> HybridRunAnywhereCore::getMemoryUsage() {
|
|
1205
|
+
return Promise<double>::async([]() {
|
|
1206
|
+
double memoryUsageMB = 0.0;
|
|
1207
|
+
|
|
1208
|
+
#if defined(__APPLE__)
|
|
1209
|
+
// iOS/macOS: Use mach_task_basic_info
|
|
1210
|
+
mach_task_basic_info_data_t taskInfo;
|
|
1211
|
+
mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
|
|
1212
|
+
|
|
1213
|
+
kern_return_t result = task_info(
|
|
1214
|
+
mach_task_self(),
|
|
1215
|
+
MACH_TASK_BASIC_INFO,
|
|
1216
|
+
reinterpret_cast<task_info_t>(&taskInfo),
|
|
1217
|
+
&infoCount
|
|
1218
|
+
);
|
|
1219
|
+
|
|
1220
|
+
if (result == KERN_SUCCESS) {
|
|
1221
|
+
// resident_size is in bytes, convert to MB
|
|
1222
|
+
memoryUsageMB = static_cast<double>(taskInfo.resident_size) / (1024.0 * 1024.0);
|
|
1223
|
+
}
|
|
1224
|
+
#elif defined(__ANDROID__) || defined(ANDROID)
|
|
1225
|
+
// Android: Read from /proc/self/status
|
|
1226
|
+
FILE* file = fopen("/proc/self/status", "r");
|
|
1227
|
+
if (file) {
|
|
1228
|
+
char line[128];
|
|
1229
|
+
while (fgets(line, sizeof(line), file)) {
|
|
1230
|
+
// Look for VmRSS (Resident Set Size)
|
|
1231
|
+
if (strncmp(line, "VmRSS:", 6) == 0) {
|
|
1232
|
+
long vmRssKB = 0;
|
|
1233
|
+
sscanf(line + 6, "%ld", &vmRssKB);
|
|
1234
|
+
memoryUsageMB = static_cast<double>(vmRssKB) / 1024.0;
|
|
1235
|
+
break;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
fclose(file);
|
|
1239
|
+
}
|
|
1240
|
+
#endif
|
|
1241
|
+
|
|
1242
|
+
LOGI("Memory usage: %.2f MB", memoryUsageMB);
|
|
1243
|
+
return memoryUsageMB;
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
// ============================================================================
|
|
1248
|
+
// Helper Methods
|
|
1249
|
+
// ============================================================================
|
|
1250
|
+
|
|
1251
|
+
void HybridRunAnywhereCore::setLastError(const std::string& error) {
|
|
1252
|
+
lastError_ = error;
|
|
1253
|
+
LOGE("%s", error.c_str());
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
// ============================================================================
|
|
1257
|
+
// LLM Capability (Backend-Agnostic)
|
|
1258
|
+
// Calls rac_llm_component_* APIs - works with any registered backend
|
|
1259
|
+
// Uses a global LLM component handle shared across HybridRunAnywhereCore instances
|
|
1260
|
+
// ============================================================================
|
|
1261
|
+
|
|
1262
|
+
// Global LLM component handle - shared across all instances
|
|
1263
|
+
static rac_handle_t g_llm_component_handle = nullptr;
|
|
1264
|
+
static std::mutex g_llm_mutex;
|
|
1265
|
+
|
|
1266
|
+
static rac_handle_t getGlobalLLMHandle() {
|
|
1267
|
+
std::lock_guard<std::mutex> lock(g_llm_mutex);
|
|
1268
|
+
if (g_llm_component_handle == nullptr) {
|
|
1269
|
+
rac_result_t result = rac_llm_component_create(&g_llm_component_handle);
|
|
1270
|
+
if (result != RAC_SUCCESS) {
|
|
1271
|
+
g_llm_component_handle = nullptr;
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
return g_llm_component_handle;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::loadTextModel(
|
|
1278
|
+
const std::string& modelPath,
|
|
1279
|
+
const std::optional<std::string>& configJson) {
|
|
1280
|
+
return Promise<bool>::async([this, modelPath, configJson]() -> bool {
|
|
1281
|
+
LOGI("Loading text model: %s", modelPath.c_str());
|
|
1282
|
+
|
|
1283
|
+
rac_handle_t handle = getGlobalLLMHandle();
|
|
1284
|
+
if (!handle) {
|
|
1285
|
+
setLastError("Failed to create LLM component. Is an LLM backend registered?");
|
|
1286
|
+
throw std::runtime_error("LLM backend not registered. Install @runanywhere/llamacpp.");
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
// Load the model
|
|
1290
|
+
rac_result_t result = rac_llm_component_load_model(handle, modelPath.c_str(), modelPath.c_str(), modelPath.c_str());
|
|
1291
|
+
if (result != RAC_SUCCESS) {
|
|
1292
|
+
setLastError("Failed to load model: " + std::to_string(result));
|
|
1293
|
+
throw std::runtime_error("Failed to load text model: " + std::to_string(result));
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
LOGI("Text model loaded successfully");
|
|
1297
|
+
return true;
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::isTextModelLoaded() {
|
|
1302
|
+
return Promise<bool>::async([]() -> bool {
|
|
1303
|
+
rac_handle_t handle = getGlobalLLMHandle();
|
|
1304
|
+
if (!handle) {
|
|
1305
|
+
return false;
|
|
1306
|
+
}
|
|
1307
|
+
bool isLoaded = rac_llm_component_is_loaded(handle) == RAC_TRUE;
|
|
1308
|
+
LOGD("isTextModelLoaded: handle=%p, isLoaded=%s", handle, isLoaded ? "true" : "false");
|
|
1309
|
+
return isLoaded;
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::unloadTextModel() {
|
|
1314
|
+
return Promise<bool>::async([]() -> bool {
|
|
1315
|
+
rac_handle_t handle = getGlobalLLMHandle();
|
|
1316
|
+
if (!handle) {
|
|
1317
|
+
return false;
|
|
1318
|
+
}
|
|
1319
|
+
rac_llm_component_cleanup(handle);
|
|
1320
|
+
// Reset global handle since model is unloaded
|
|
1321
|
+
{
|
|
1322
|
+
std::lock_guard<std::mutex> lock(g_llm_mutex);
|
|
1323
|
+
g_llm_component_handle = nullptr;
|
|
1324
|
+
}
|
|
1325
|
+
return true;
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::generate(
|
|
1330
|
+
const std::string& prompt,
|
|
1331
|
+
const std::optional<std::string>& optionsJson) {
|
|
1332
|
+
return Promise<std::string>::async([this, prompt, optionsJson]() -> std::string {
|
|
1333
|
+
LOGI("Generating text...");
|
|
1334
|
+
|
|
1335
|
+
rac_handle_t handle = getGlobalLLMHandle();
|
|
1336
|
+
if (!handle) {
|
|
1337
|
+
throw std::runtime_error("LLM component not available. Is an LLM backend registered?");
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
if (rac_llm_component_is_loaded(handle) != RAC_TRUE) {
|
|
1341
|
+
throw std::runtime_error("No LLM model loaded. Call loadTextModel first.");
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
// Parse options
|
|
1345
|
+
int maxTokens = 256;
|
|
1346
|
+
float temperature = 0.7f;
|
|
1347
|
+
if (optionsJson.has_value()) {
|
|
1348
|
+
maxTokens = extractIntValue(optionsJson.value(), "max_tokens", 256);
|
|
1349
|
+
temperature = static_cast<float>(extractDoubleValue(optionsJson.value(), "temperature", 0.7));
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
rac_llm_options_t options = {};
|
|
1353
|
+
options.max_tokens = maxTokens;
|
|
1354
|
+
options.temperature = temperature;
|
|
1355
|
+
options.top_p = 0.9f;
|
|
1356
|
+
|
|
1357
|
+
rac_llm_result_t llmResult = {};
|
|
1358
|
+
rac_result_t result = rac_llm_component_generate(handle, prompt.c_str(), &options, &llmResult);
|
|
1359
|
+
|
|
1360
|
+
if (result != RAC_SUCCESS) {
|
|
1361
|
+
throw std::runtime_error("Text generation failed: " + std::to_string(result));
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
std::string text = llmResult.text ? llmResult.text : "";
|
|
1365
|
+
int tokensUsed = llmResult.completion_tokens;
|
|
1366
|
+
|
|
1367
|
+
return buildJsonObject({
|
|
1368
|
+
{"text", jsonString(text)},
|
|
1369
|
+
{"tokensUsed", std::to_string(tokensUsed)},
|
|
1370
|
+
{"modelUsed", jsonString("llm")},
|
|
1371
|
+
{"latencyMs", std::to_string(llmResult.total_time_ms)}
|
|
1372
|
+
});
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
// Streaming context for LLM callbacks
|
|
1377
|
+
struct LLMStreamContext {
|
|
1378
|
+
std::function<void(const std::string&, bool)> callback;
|
|
1379
|
+
std::string accumulatedText;
|
|
1380
|
+
int tokenCount = 0;
|
|
1381
|
+
bool hasError = false;
|
|
1382
|
+
std::string errorMessage;
|
|
1383
|
+
rac_llm_result_t finalResult = {};
|
|
1384
|
+
};
|
|
1385
|
+
|
|
1386
|
+
// Token callback for streaming
|
|
1387
|
+
static rac_bool_t llmStreamTokenCallback(const char* token, void* userData) {
|
|
1388
|
+
auto* ctx = static_cast<LLMStreamContext*>(userData);
|
|
1389
|
+
if (!ctx || !token) return RAC_FALSE;
|
|
1390
|
+
|
|
1391
|
+
std::string tokenStr(token);
|
|
1392
|
+
ctx->accumulatedText += tokenStr;
|
|
1393
|
+
ctx->tokenCount++;
|
|
1394
|
+
|
|
1395
|
+
// Call the JS callback with partial text (not final)
|
|
1396
|
+
if (ctx->callback) {
|
|
1397
|
+
ctx->callback(tokenStr, false);
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
return RAC_TRUE; // Continue streaming
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
// Complete callback for streaming
|
|
1404
|
+
static void llmStreamCompleteCallback(const rac_llm_result_t* result, void* userData) {
|
|
1405
|
+
auto* ctx = static_cast<LLMStreamContext*>(userData);
|
|
1406
|
+
if (!ctx) return;
|
|
1407
|
+
|
|
1408
|
+
if (result) {
|
|
1409
|
+
ctx->finalResult = *result;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
// Call callback with final signal
|
|
1413
|
+
if (ctx->callback) {
|
|
1414
|
+
ctx->callback("", true);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
// Error callback for streaming
|
|
1419
|
+
static void llmStreamErrorCallback(rac_result_t errorCode, const char* errorMessage, void* userData) {
|
|
1420
|
+
auto* ctx = static_cast<LLMStreamContext*>(userData);
|
|
1421
|
+
if (!ctx) return;
|
|
1422
|
+
|
|
1423
|
+
ctx->hasError = true;
|
|
1424
|
+
ctx->errorMessage = errorMessage ? std::string(errorMessage) : "Unknown streaming error";
|
|
1425
|
+
LOGE("LLM streaming error: %d - %s", errorCode, ctx->errorMessage.c_str());
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::generateStream(
|
|
1429
|
+
const std::string& prompt,
|
|
1430
|
+
const std::string& optionsJson,
|
|
1431
|
+
const std::function<void(const std::string&, bool)>& callback) {
|
|
1432
|
+
return Promise<std::string>::async([this, prompt, optionsJson, callback]() -> std::string {
|
|
1433
|
+
LOGI("Streaming text generation...");
|
|
1434
|
+
|
|
1435
|
+
rac_handle_t handle = getGlobalLLMHandle();
|
|
1436
|
+
if (!handle) {
|
|
1437
|
+
throw std::runtime_error("LLM component not available. Is an LLM backend registered?");
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
if (rac_llm_component_is_loaded(handle) != RAC_TRUE) {
|
|
1441
|
+
throw std::runtime_error("No LLM model loaded. Call loadTextModel first.");
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
// Parse options
|
|
1445
|
+
rac_llm_options_t options = {};
|
|
1446
|
+
options.max_tokens = extractIntValue(optionsJson, "max_tokens", 256);
|
|
1447
|
+
options.temperature = static_cast<float>(extractDoubleValue(optionsJson, "temperature", 0.7));
|
|
1448
|
+
options.top_p = 0.9f;
|
|
1449
|
+
|
|
1450
|
+
// Create streaming context
|
|
1451
|
+
LLMStreamContext ctx;
|
|
1452
|
+
ctx.callback = callback;
|
|
1453
|
+
|
|
1454
|
+
// Use proper streaming API
|
|
1455
|
+
rac_result_t result = rac_llm_component_generate_stream(
|
|
1456
|
+
handle,
|
|
1457
|
+
prompt.c_str(),
|
|
1458
|
+
&options,
|
|
1459
|
+
llmStreamTokenCallback,
|
|
1460
|
+
llmStreamCompleteCallback,
|
|
1461
|
+
llmStreamErrorCallback,
|
|
1462
|
+
&ctx
|
|
1463
|
+
);
|
|
1464
|
+
|
|
1465
|
+
if (result != RAC_SUCCESS) {
|
|
1466
|
+
throw std::runtime_error("Streaming generation failed: " + std::to_string(result));
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
if (ctx.hasError) {
|
|
1470
|
+
throw std::runtime_error("Streaming error: " + ctx.errorMessage);
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
LOGI("Streaming complete: %zu chars, %d tokens", ctx.accumulatedText.size(), ctx.tokenCount);
|
|
1474
|
+
|
|
1475
|
+
return buildJsonObject({
|
|
1476
|
+
{"text", jsonString(ctx.accumulatedText)},
|
|
1477
|
+
{"tokensUsed", std::to_string(ctx.tokenCount)}
|
|
1478
|
+
});
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::cancelGeneration() {
|
|
1483
|
+
return Promise<bool>::async([]() -> bool {
|
|
1484
|
+
rac_handle_t handle = getGlobalLLMHandle();
|
|
1485
|
+
if (!handle) {
|
|
1486
|
+
return false;
|
|
1487
|
+
}
|
|
1488
|
+
rac_llm_component_cancel(handle);
|
|
1489
|
+
return true;
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::generateStructured(
|
|
1494
|
+
const std::string& prompt,
|
|
1495
|
+
const std::string& schema,
|
|
1496
|
+
const std::optional<std::string>& optionsJson) {
|
|
1497
|
+
return Promise<std::string>::async([this, prompt, schema, optionsJson]() -> std::string {
|
|
1498
|
+
LOGI("Generating structured output...");
|
|
1499
|
+
|
|
1500
|
+
rac_handle_t handle = getGlobalLLMHandle();
|
|
1501
|
+
if (!handle) {
|
|
1502
|
+
throw std::runtime_error("LLM component not available. Is an LLM backend registered?");
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
if (rac_llm_component_is_loaded(handle) != RAC_TRUE) {
|
|
1506
|
+
throw std::runtime_error("No LLM model loaded. Call loadTextModel first.");
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
// Prepare the prompt with the schema embedded
|
|
1510
|
+
rac_structured_output_config_t config = RAC_STRUCTURED_OUTPUT_DEFAULT;
|
|
1511
|
+
config.json_schema = schema.c_str();
|
|
1512
|
+
config.include_schema_in_prompt = RAC_TRUE;
|
|
1513
|
+
|
|
1514
|
+
char* preparedPrompt = nullptr;
|
|
1515
|
+
rac_result_t prepResult = rac_structured_output_prepare_prompt(prompt.c_str(), &config, &preparedPrompt);
|
|
1516
|
+
if (prepResult != RAC_SUCCESS || !preparedPrompt) {
|
|
1517
|
+
throw std::runtime_error("Failed to prepare structured output prompt");
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
// Generate with the prepared prompt
|
|
1521
|
+
rac_llm_options_t options = {};
|
|
1522
|
+
if (optionsJson.has_value()) {
|
|
1523
|
+
options.max_tokens = extractIntValue(optionsJson.value(), "max_tokens", 512);
|
|
1524
|
+
options.temperature = static_cast<float>(extractDoubleValue(optionsJson.value(), "temperature", 0.7));
|
|
1525
|
+
} else {
|
|
1526
|
+
options.max_tokens = 512;
|
|
1527
|
+
options.temperature = 0.7f;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
rac_llm_result_t llmResult = {};
|
|
1531
|
+
rac_result_t result = rac_llm_component_generate(handle, preparedPrompt, &options, &llmResult);
|
|
1532
|
+
|
|
1533
|
+
free(preparedPrompt);
|
|
1534
|
+
|
|
1535
|
+
if (result != RAC_SUCCESS) {
|
|
1536
|
+
throw std::runtime_error("Text generation failed: " + std::to_string(result));
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
std::string generatedText;
|
|
1540
|
+
if (llmResult.text) {
|
|
1541
|
+
generatedText = std::string(llmResult.text);
|
|
1542
|
+
}
|
|
1543
|
+
rac_llm_result_free(&llmResult);
|
|
1544
|
+
|
|
1545
|
+
// Extract JSON from the generated text
|
|
1546
|
+
char* extractedJson = nullptr;
|
|
1547
|
+
rac_result_t extractResult = rac_structured_output_extract_json(generatedText.c_str(), &extractedJson, nullptr);
|
|
1548
|
+
|
|
1549
|
+
if (extractResult == RAC_SUCCESS && extractedJson) {
|
|
1550
|
+
std::string jsonOutput = std::string(extractedJson);
|
|
1551
|
+
free(extractedJson);
|
|
1552
|
+
LOGI("Extracted structured JSON: %s", jsonOutput.substr(0, 100).c_str());
|
|
1553
|
+
return jsonOutput;
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
// If extraction failed, return the raw text (let the caller handle it)
|
|
1557
|
+
LOGI("Could not extract JSON, returning raw: %s", generatedText.substr(0, 100).c_str());
|
|
1558
|
+
return generatedText;
|
|
1559
|
+
});
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
// ============================================================================
|
|
1563
|
+
// STT Capability (Backend-Agnostic)
|
|
1564
|
+
// Calls rac_stt_component_* APIs - works with any registered backend
|
|
1565
|
+
// Uses a global STT component handle shared across HybridRunAnywhereCore instances
|
|
1566
|
+
// ============================================================================
|
|
1567
|
+
|
|
1568
|
+
// Global STT component handle - shared across all instances
|
|
1569
|
+
// This ensures model loading state persists even when HybridRunAnywhereCore instances are recreated
|
|
1570
|
+
static rac_handle_t g_stt_component_handle = nullptr;
|
|
1571
|
+
static std::mutex g_stt_mutex;
|
|
1572
|
+
|
|
1573
|
+
static rac_handle_t getGlobalSTTHandle() {
|
|
1574
|
+
std::lock_guard<std::mutex> lock(g_stt_mutex);
|
|
1575
|
+
if (g_stt_component_handle == nullptr) {
|
|
1576
|
+
rac_result_t result = rac_stt_component_create(&g_stt_component_handle);
|
|
1577
|
+
if (result != RAC_SUCCESS) {
|
|
1578
|
+
g_stt_component_handle = nullptr;
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1581
|
+
return g_stt_component_handle;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::loadSTTModel(
|
|
1585
|
+
const std::string& modelPath,
|
|
1586
|
+
const std::string& modelType,
|
|
1587
|
+
const std::optional<std::string>& configJson) {
|
|
1588
|
+
return Promise<bool>::async([this, modelPath, modelType]() -> bool {
|
|
1589
|
+
LOGI("Loading STT model: %s", modelPath.c_str());
|
|
1590
|
+
|
|
1591
|
+
rac_handle_t handle = getGlobalSTTHandle();
|
|
1592
|
+
if (!handle) {
|
|
1593
|
+
setLastError("Failed to create STT component. Is an STT backend registered?");
|
|
1594
|
+
throw std::runtime_error("STT backend not registered. Install @runanywhere/onnx.");
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
rac_result_t result = rac_stt_component_load_model(handle, modelPath.c_str(), modelPath.c_str(), modelType.c_str());
|
|
1598
|
+
if (result != RAC_SUCCESS) {
|
|
1599
|
+
throw std::runtime_error("Failed to load STT model: " + std::to_string(result));
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
LOGI("STT model loaded successfully");
|
|
1603
|
+
return true;
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::isSTTModelLoaded() {
|
|
1608
|
+
return Promise<bool>::async([]() -> bool {
|
|
1609
|
+
rac_handle_t handle = getGlobalSTTHandle();
|
|
1610
|
+
if (!handle) {
|
|
1611
|
+
return false;
|
|
1612
|
+
}
|
|
1613
|
+
bool isLoaded = rac_stt_component_is_loaded(handle) == RAC_TRUE;
|
|
1614
|
+
LOGD("isSTTModelLoaded: handle=%p, isLoaded=%s", handle, isLoaded ? "true" : "false");
|
|
1615
|
+
return isLoaded;
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::unloadSTTModel() {
|
|
1620
|
+
return Promise<bool>::async([]() -> bool {
|
|
1621
|
+
rac_handle_t handle = getGlobalSTTHandle();
|
|
1622
|
+
if (!handle) {
|
|
1623
|
+
return false;
|
|
1624
|
+
}
|
|
1625
|
+
rac_stt_component_cleanup(handle);
|
|
1626
|
+
// Reset global handle since model is unloaded
|
|
1627
|
+
{
|
|
1628
|
+
std::lock_guard<std::mutex> lock(g_stt_mutex);
|
|
1629
|
+
g_stt_component_handle = nullptr;
|
|
1630
|
+
}
|
|
1631
|
+
return true;
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::transcribe(
|
|
1636
|
+
const std::string& audioBase64,
|
|
1637
|
+
double sampleRate,
|
|
1638
|
+
const std::optional<std::string>& language) {
|
|
1639
|
+
return Promise<std::string>::async([this, audioBase64, sampleRate, language]() -> std::string {
|
|
1640
|
+
LOGI("Transcribing audio (base64)...");
|
|
1641
|
+
|
|
1642
|
+
rac_handle_t handle = getGlobalSTTHandle();
|
|
1643
|
+
if (!handle) {
|
|
1644
|
+
throw std::runtime_error("STT component not available. Is an STT backend registered?");
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
if (rac_stt_component_is_loaded(handle) != RAC_TRUE) {
|
|
1648
|
+
throw std::runtime_error("No STT model loaded. Call loadSTTModel first.");
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
// Decode base64 audio data
|
|
1652
|
+
std::vector<uint8_t> audioData = base64Decode(audioBase64);
|
|
1653
|
+
if (audioData.empty()) {
|
|
1654
|
+
throw std::runtime_error("Failed to decode base64 audio data");
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
LOGI("Decoded %zu bytes of audio data", audioData.size());
|
|
1658
|
+
|
|
1659
|
+
// Set up transcription options
|
|
1660
|
+
rac_stt_options_t options = RAC_STT_OPTIONS_DEFAULT;
|
|
1661
|
+
options.sample_rate = static_cast<int32_t>(sampleRate > 0 ? sampleRate : 16000);
|
|
1662
|
+
options.audio_format = RAC_AUDIO_FORMAT_PCM;
|
|
1663
|
+
if (language.has_value() && !language->empty()) {
|
|
1664
|
+
options.language = language->c_str();
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
// Transcribe
|
|
1668
|
+
rac_stt_result_t result = {};
|
|
1669
|
+
rac_result_t status = rac_stt_component_transcribe(
|
|
1670
|
+
handle,
|
|
1671
|
+
audioData.data(),
|
|
1672
|
+
audioData.size(),
|
|
1673
|
+
&options,
|
|
1674
|
+
&result
|
|
1675
|
+
);
|
|
1676
|
+
|
|
1677
|
+
if (status != RAC_SUCCESS) {
|
|
1678
|
+
throw std::runtime_error("Transcription failed with error code: " + std::to_string(status));
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
std::string transcribedText;
|
|
1682
|
+
if (result.text) {
|
|
1683
|
+
transcribedText = std::string(result.text);
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
// Free the result
|
|
1687
|
+
rac_stt_result_free(&result);
|
|
1688
|
+
|
|
1689
|
+
LOGI("Transcription result: %s", transcribedText.c_str());
|
|
1690
|
+
return transcribedText;
|
|
1691
|
+
});
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::transcribeFile(
|
|
1695
|
+
const std::string& filePath,
|
|
1696
|
+
const std::optional<std::string>& language) {
|
|
1697
|
+
return Promise<std::string>::async([this, filePath, language]() -> std::string {
|
|
1698
|
+
LOGI("Transcribing file: %s", filePath.c_str());
|
|
1699
|
+
|
|
1700
|
+
rac_handle_t handle = getGlobalSTTHandle();
|
|
1701
|
+
if (!handle) {
|
|
1702
|
+
throw std::runtime_error("STT component not available. Is an STT backend registered?");
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
if (rac_stt_component_is_loaded(handle) != RAC_TRUE) {
|
|
1706
|
+
throw std::runtime_error("No STT model loaded. Call loadSTTModel first.");
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
// Open the file
|
|
1710
|
+
FILE* file = fopen(filePath.c_str(), "rb");
|
|
1711
|
+
if (!file) {
|
|
1712
|
+
throw std::runtime_error("Failed to open audio file: " + filePath);
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
// Get file size
|
|
1716
|
+
fseek(file, 0, SEEK_END);
|
|
1717
|
+
long fileSize = ftell(file);
|
|
1718
|
+
fseek(file, 0, SEEK_SET);
|
|
1719
|
+
|
|
1720
|
+
if (fileSize <= 0) {
|
|
1721
|
+
fclose(file);
|
|
1722
|
+
throw std::runtime_error("Audio file is empty: " + filePath);
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
LOGI("File size: %ld bytes", fileSize);
|
|
1726
|
+
|
|
1727
|
+
// Read the entire file into memory
|
|
1728
|
+
std::vector<uint8_t> fileData(fileSize);
|
|
1729
|
+
size_t bytesRead = fread(fileData.data(), 1, fileSize, file);
|
|
1730
|
+
fclose(file);
|
|
1731
|
+
|
|
1732
|
+
if (bytesRead != static_cast<size_t>(fileSize)) {
|
|
1733
|
+
throw std::runtime_error("Failed to read audio file completely");
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
// Parse WAV header to extract audio data
|
|
1737
|
+
// WAV header: RIFF chunk (12 bytes) + fmt chunk + data chunk
|
|
1738
|
+
// We need to find the "data" chunk and extract PCM audio
|
|
1739
|
+
|
|
1740
|
+
const uint8_t* data = fileData.data();
|
|
1741
|
+
size_t dataSize = fileData.size();
|
|
1742
|
+
int32_t sampleRate = 16000;
|
|
1743
|
+
|
|
1744
|
+
// Check RIFF header
|
|
1745
|
+
if (dataSize < 44) {
|
|
1746
|
+
throw std::runtime_error("File too small to be a valid WAV file");
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
// Check "RIFF" signature
|
|
1750
|
+
if (data[0] != 'R' || data[1] != 'I' || data[2] != 'F' || data[3] != 'F') {
|
|
1751
|
+
throw std::runtime_error("Invalid WAV file: missing RIFF header");
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
// Check "WAVE" format
|
|
1755
|
+
if (data[8] != 'W' || data[9] != 'A' || data[10] != 'V' || data[11] != 'E') {
|
|
1756
|
+
throw std::runtime_error("Invalid WAV file: missing WAVE format");
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
// Find "fmt " and "data" chunks
|
|
1760
|
+
size_t pos = 12;
|
|
1761
|
+
size_t audioDataOffset = 0;
|
|
1762
|
+
size_t audioDataSize = 0;
|
|
1763
|
+
|
|
1764
|
+
while (pos + 8 < dataSize) {
|
|
1765
|
+
char chunkId[5] = {0};
|
|
1766
|
+
memcpy(chunkId, &data[pos], 4);
|
|
1767
|
+
uint32_t chunkSize = *reinterpret_cast<const uint32_t*>(&data[pos + 4]);
|
|
1768
|
+
|
|
1769
|
+
if (strcmp(chunkId, "fmt ") == 0) {
|
|
1770
|
+
// Parse fmt chunk
|
|
1771
|
+
if (pos + 8 + chunkSize <= dataSize && chunkSize >= 16) {
|
|
1772
|
+
// Bytes 12-13: Audio format (1 = PCM)
|
|
1773
|
+
// Bytes 14-15: Number of channels
|
|
1774
|
+
// Bytes 16-19: Sample rate
|
|
1775
|
+
sampleRate = *reinterpret_cast<const int32_t*>(&data[pos + 12]);
|
|
1776
|
+
LOGI("WAV sample rate: %d Hz", sampleRate);
|
|
1777
|
+
}
|
|
1778
|
+
} else if (strcmp(chunkId, "data") == 0) {
|
|
1779
|
+
// Found data chunk
|
|
1780
|
+
audioDataOffset = pos + 8;
|
|
1781
|
+
audioDataSize = chunkSize;
|
|
1782
|
+
LOGI("Found audio data: offset=%zu, size=%zu", audioDataOffset, audioDataSize);
|
|
1783
|
+
break;
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
pos += 8 + chunkSize;
|
|
1787
|
+
// Align to 2-byte boundary
|
|
1788
|
+
if (chunkSize % 2 != 0) pos++;
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
if (audioDataSize == 0 || audioDataOffset + audioDataSize > dataSize) {
|
|
1792
|
+
throw std::runtime_error("Could not find valid audio data in WAV file");
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
// Set up transcription options
|
|
1796
|
+
rac_stt_options_t options = RAC_STT_OPTIONS_DEFAULT;
|
|
1797
|
+
options.sample_rate = sampleRate;
|
|
1798
|
+
options.audio_format = RAC_AUDIO_FORMAT_WAV; // Tell the backend it's WAV format
|
|
1799
|
+
if (language.has_value() && !language->empty()) {
|
|
1800
|
+
options.language = language->c_str();
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
LOGI("Transcribing %zu bytes of audio at %d Hz", audioDataSize, sampleRate);
|
|
1804
|
+
|
|
1805
|
+
// Transcribe - pass the raw PCM data (after WAV header)
|
|
1806
|
+
rac_stt_result_t result = {};
|
|
1807
|
+
rac_result_t status = rac_stt_component_transcribe(
|
|
1808
|
+
handle,
|
|
1809
|
+
&data[audioDataOffset],
|
|
1810
|
+
audioDataSize,
|
|
1811
|
+
&options,
|
|
1812
|
+
&result
|
|
1813
|
+
);
|
|
1814
|
+
|
|
1815
|
+
if (status != RAC_SUCCESS) {
|
|
1816
|
+
throw std::runtime_error("Transcription failed with error code: " + std::to_string(status));
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
std::string transcribedText;
|
|
1820
|
+
if (result.text) {
|
|
1821
|
+
transcribedText = std::string(result.text);
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
// Free the result
|
|
1825
|
+
rac_stt_result_free(&result);
|
|
1826
|
+
|
|
1827
|
+
LOGI("Transcription result: %s", transcribedText.c_str());
|
|
1828
|
+
return transcribedText;
|
|
1829
|
+
});
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
// ============================================================================
|
|
1833
|
+
// TTS Capability (Backend-Agnostic)
|
|
1834
|
+
// Calls rac_tts_component_* APIs - works with any registered backend
|
|
1835
|
+
// Uses a global TTS component handle shared across HybridRunAnywhereCore instances
|
|
1836
|
+
// ============================================================================
|
|
1837
|
+
|
|
1838
|
+
// Global TTS component handle - shared across all instances
|
|
1839
|
+
static rac_handle_t g_tts_component_handle = nullptr;
|
|
1840
|
+
static std::mutex g_tts_mutex;
|
|
1841
|
+
|
|
1842
|
+
static rac_handle_t getGlobalTTSHandle() {
|
|
1843
|
+
std::lock_guard<std::mutex> lock(g_tts_mutex);
|
|
1844
|
+
if (g_tts_component_handle == nullptr) {
|
|
1845
|
+
rac_result_t result = rac_tts_component_create(&g_tts_component_handle);
|
|
1846
|
+
if (result != RAC_SUCCESS) {
|
|
1847
|
+
g_tts_component_handle = nullptr;
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
return g_tts_component_handle;
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::loadTTSModel(
|
|
1854
|
+
const std::string& modelPath,
|
|
1855
|
+
const std::string& modelType,
|
|
1856
|
+
const std::optional<std::string>& configJson) {
|
|
1857
|
+
return Promise<bool>::async([this, modelPath, modelType]() -> bool {
|
|
1858
|
+
LOGI("Loading TTS model: %s", modelPath.c_str());
|
|
1859
|
+
|
|
1860
|
+
rac_handle_t handle = getGlobalTTSHandle();
|
|
1861
|
+
if (!handle) {
|
|
1862
|
+
setLastError("Failed to create TTS component. Is a TTS backend registered?");
|
|
1863
|
+
throw std::runtime_error("TTS backend not registered. Install @runanywhere/onnx.");
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
// Configure the TTS component first
|
|
1867
|
+
rac_tts_config_t config = RAC_TTS_CONFIG_DEFAULT;
|
|
1868
|
+
config.model_id = modelPath.c_str();
|
|
1869
|
+
rac_result_t result = rac_tts_component_configure(handle, &config);
|
|
1870
|
+
if (result != RAC_SUCCESS) {
|
|
1871
|
+
LOGE("TTS configure failed: %d", result);
|
|
1872
|
+
throw std::runtime_error("Failed to configure TTS: " + std::to_string(result));
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
// Extract model ID from path for telemetry
|
|
1876
|
+
std::string voiceId = modelPath;
|
|
1877
|
+
size_t lastSlash = voiceId.find_last_of('/');
|
|
1878
|
+
if (lastSlash != std::string::npos) {
|
|
1879
|
+
voiceId = voiceId.substr(lastSlash + 1);
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
// Load the voice - this is what actually loads the model files
|
|
1883
|
+
result = rac_tts_component_load_voice(handle, modelPath.c_str(), voiceId.c_str(), modelType.c_str());
|
|
1884
|
+
if (result != RAC_SUCCESS) {
|
|
1885
|
+
LOGE("TTS load_voice failed: %d", result);
|
|
1886
|
+
throw std::runtime_error("Failed to load TTS voice: " + std::to_string(result));
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
// Verify loading
|
|
1890
|
+
bool isLoaded = rac_tts_component_is_loaded(handle) == RAC_TRUE;
|
|
1891
|
+
LOGI("TTS model loaded successfully, isLoaded=%s", isLoaded ? "true" : "false");
|
|
1892
|
+
|
|
1893
|
+
return isLoaded;
|
|
1894
|
+
});
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::isTTSModelLoaded() {
|
|
1898
|
+
return Promise<bool>::async([]() -> bool {
|
|
1899
|
+
rac_handle_t handle = getGlobalTTSHandle();
|
|
1900
|
+
if (!handle) {
|
|
1901
|
+
return false;
|
|
1902
|
+
}
|
|
1903
|
+
bool isLoaded = rac_tts_component_is_loaded(handle) == RAC_TRUE;
|
|
1904
|
+
LOGD("isTTSModelLoaded: handle=%p, isLoaded=%s", handle, isLoaded ? "true" : "false");
|
|
1905
|
+
return isLoaded;
|
|
1906
|
+
});
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::unloadTTSModel() {
|
|
1910
|
+
return Promise<bool>::async([]() -> bool {
|
|
1911
|
+
rac_handle_t handle = getGlobalTTSHandle();
|
|
1912
|
+
if (!handle) {
|
|
1913
|
+
return false;
|
|
1914
|
+
}
|
|
1915
|
+
rac_tts_component_cleanup(handle);
|
|
1916
|
+
// Reset global handle since model is unloaded
|
|
1917
|
+
{
|
|
1918
|
+
std::lock_guard<std::mutex> lock(g_tts_mutex);
|
|
1919
|
+
g_tts_component_handle = nullptr;
|
|
1920
|
+
}
|
|
1921
|
+
return true;
|
|
1922
|
+
});
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::synthesize(
|
|
1926
|
+
const std::string& text,
|
|
1927
|
+
const std::string& voiceId,
|
|
1928
|
+
double speedRate,
|
|
1929
|
+
double pitchShift) {
|
|
1930
|
+
return Promise<std::string>::async([this, text, voiceId, speedRate, pitchShift]() -> std::string {
|
|
1931
|
+
LOGI("Synthesizing speech: %s", text.substr(0, 50).c_str());
|
|
1932
|
+
|
|
1933
|
+
rac_handle_t handle = getGlobalTTSHandle();
|
|
1934
|
+
if (!handle) {
|
|
1935
|
+
throw std::runtime_error("TTS component not available. Is a TTS backend registered?");
|
|
1936
|
+
}
|
|
1937
|
+
|
|
1938
|
+
if (rac_tts_component_is_loaded(handle) != RAC_TRUE) {
|
|
1939
|
+
throw std::runtime_error("No TTS model loaded. Call loadTTSModel first.");
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
// Set up synthesis options
|
|
1943
|
+
rac_tts_options_t options = RAC_TTS_OPTIONS_DEFAULT;
|
|
1944
|
+
if (!voiceId.empty()) {
|
|
1945
|
+
options.voice = voiceId.c_str();
|
|
1946
|
+
}
|
|
1947
|
+
options.rate = static_cast<float>(speedRate > 0 ? speedRate : 1.0);
|
|
1948
|
+
options.pitch = static_cast<float>(pitchShift > 0 ? pitchShift : 1.0);
|
|
1949
|
+
|
|
1950
|
+
// Synthesize
|
|
1951
|
+
rac_tts_result_t result = {};
|
|
1952
|
+
rac_result_t status = rac_tts_component_synthesize(handle, text.c_str(), &options, &result);
|
|
1953
|
+
|
|
1954
|
+
if (status != RAC_SUCCESS) {
|
|
1955
|
+
throw std::runtime_error("TTS synthesis failed with error code: " + std::to_string(status));
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
if (!result.audio_data || result.audio_size == 0) {
|
|
1959
|
+
rac_tts_result_free(&result);
|
|
1960
|
+
throw std::runtime_error("TTS synthesis returned no audio data");
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
LOGI("TTS synthesis complete: %zu bytes, %d Hz, %lld ms",
|
|
1964
|
+
result.audio_size, result.sample_rate, result.duration_ms);
|
|
1965
|
+
|
|
1966
|
+
// Convert audio data to base64
|
|
1967
|
+
std::string audioBase64 = base64Encode(
|
|
1968
|
+
static_cast<const uint8_t*>(result.audio_data),
|
|
1969
|
+
result.audio_size
|
|
1970
|
+
);
|
|
1971
|
+
|
|
1972
|
+
// Build JSON result with metadata
|
|
1973
|
+
std::ostringstream json;
|
|
1974
|
+
json << "{";
|
|
1975
|
+
json << "\"audioBase64\":\"" << audioBase64 << "\",";
|
|
1976
|
+
json << "\"sampleRate\":" << result.sample_rate << ",";
|
|
1977
|
+
json << "\"durationMs\":" << result.duration_ms << ",";
|
|
1978
|
+
json << "\"audioSize\":" << result.audio_size;
|
|
1979
|
+
json << "}";
|
|
1980
|
+
|
|
1981
|
+
rac_tts_result_free(&result);
|
|
1982
|
+
|
|
1983
|
+
return json.str();
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getTTSVoices() {
|
|
1988
|
+
return Promise<std::string>::async([]() -> std::string {
|
|
1989
|
+
return "[]"; // Return empty array for now
|
|
1990
|
+
});
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::cancelTTS() {
|
|
1994
|
+
return Promise<bool>::async([]() -> bool {
|
|
1995
|
+
return true;
|
|
1996
|
+
});
|
|
1997
|
+
}
|
|
1998
|
+
|
|
1999
|
+
// ============================================================================
|
|
2000
|
+
// VAD Capability (Backend-Agnostic)
|
|
2001
|
+
// Calls rac_vad_component_* APIs - works with any registered backend
|
|
2002
|
+
// Uses a global VAD component handle shared across HybridRunAnywhereCore instances
|
|
2003
|
+
// ============================================================================
|
|
2004
|
+
|
|
2005
|
+
// Global VAD component handle - shared across all instances
|
|
2006
|
+
static rac_handle_t g_vad_component_handle = nullptr;
|
|
2007
|
+
static std::mutex g_vad_mutex;
|
|
2008
|
+
|
|
2009
|
+
static rac_handle_t getGlobalVADHandle() {
|
|
2010
|
+
std::lock_guard<std::mutex> lock(g_vad_mutex);
|
|
2011
|
+
if (g_vad_component_handle == nullptr) {
|
|
2012
|
+
rac_result_t result = rac_vad_component_create(&g_vad_component_handle);
|
|
2013
|
+
if (result != RAC_SUCCESS) {
|
|
2014
|
+
g_vad_component_handle = nullptr;
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
return g_vad_component_handle;
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::loadVADModel(
|
|
2021
|
+
const std::string& modelPath,
|
|
2022
|
+
const std::optional<std::string>& configJson) {
|
|
2023
|
+
return Promise<bool>::async([this, modelPath]() -> bool {
|
|
2024
|
+
LOGI("Loading VAD model: %s", modelPath.c_str());
|
|
2025
|
+
|
|
2026
|
+
rac_handle_t handle = getGlobalVADHandle();
|
|
2027
|
+
if (!handle) {
|
|
2028
|
+
setLastError("Failed to create VAD component. Is a VAD backend registered?");
|
|
2029
|
+
throw std::runtime_error("VAD backend not registered. Install @runanywhere/onnx.");
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
rac_vad_config_t config = RAC_VAD_CONFIG_DEFAULT;
|
|
2033
|
+
config.model_id = modelPath.c_str();
|
|
2034
|
+
rac_result_t result = rac_vad_component_configure(handle, &config);
|
|
2035
|
+
if (result != RAC_SUCCESS) {
|
|
2036
|
+
throw std::runtime_error("Failed to configure VAD: " + std::to_string(result));
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
result = rac_vad_component_initialize(handle);
|
|
2040
|
+
if (result != RAC_SUCCESS) {
|
|
2041
|
+
throw std::runtime_error("Failed to initialize VAD: " + std::to_string(result));
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
LOGI("VAD model loaded successfully");
|
|
2045
|
+
return true;
|
|
2046
|
+
});
|
|
2047
|
+
}
|
|
2048
|
+
|
|
2049
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::isVADModelLoaded() {
|
|
2050
|
+
return Promise<bool>::async([]() -> bool {
|
|
2051
|
+
rac_handle_t handle = getGlobalVADHandle();
|
|
2052
|
+
if (!handle) {
|
|
2053
|
+
return false;
|
|
2054
|
+
}
|
|
2055
|
+
bool isLoaded = rac_vad_component_is_initialized(handle) == RAC_TRUE;
|
|
2056
|
+
LOGD("isVADModelLoaded: handle=%p, isLoaded=%s", handle, isLoaded ? "true" : "false");
|
|
2057
|
+
return isLoaded;
|
|
2058
|
+
});
|
|
2059
|
+
}
|
|
2060
|
+
|
|
2061
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::unloadVADModel() {
|
|
2062
|
+
return Promise<bool>::async([]() -> bool {
|
|
2063
|
+
rac_handle_t handle = getGlobalVADHandle();
|
|
2064
|
+
if (!handle) {
|
|
2065
|
+
return false;
|
|
2066
|
+
}
|
|
2067
|
+
rac_vad_component_cleanup(handle);
|
|
2068
|
+
// Reset global handle since model is unloaded
|
|
2069
|
+
{
|
|
2070
|
+
std::lock_guard<std::mutex> lock(g_vad_mutex);
|
|
2071
|
+
g_vad_component_handle = nullptr;
|
|
2072
|
+
}
|
|
2073
|
+
return true;
|
|
2074
|
+
});
|
|
2075
|
+
}
|
|
2076
|
+
|
|
2077
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::processVAD(
|
|
2078
|
+
const std::string& audioBase64,
|
|
2079
|
+
const std::optional<std::string>& optionsJson) {
|
|
2080
|
+
return Promise<std::string>::async([this, audioBase64, optionsJson]() -> std::string {
|
|
2081
|
+
LOGI("Processing VAD...");
|
|
2082
|
+
|
|
2083
|
+
rac_handle_t handle = getGlobalVADHandle();
|
|
2084
|
+
if (!handle) {
|
|
2085
|
+
throw std::runtime_error("VAD component not available. Is a VAD backend registered?");
|
|
2086
|
+
}
|
|
2087
|
+
|
|
2088
|
+
// Decode base64 audio data
|
|
2089
|
+
std::vector<uint8_t> audioData = base64Decode(audioBase64);
|
|
2090
|
+
if (audioData.empty()) {
|
|
2091
|
+
throw std::runtime_error("Failed to decode base64 audio data for VAD");
|
|
2092
|
+
}
|
|
2093
|
+
|
|
2094
|
+
// Convert byte data to float samples
|
|
2095
|
+
// Assuming 16-bit PCM audio: 2 bytes per sample
|
|
2096
|
+
size_t numSamples = audioData.size() / sizeof(int16_t);
|
|
2097
|
+
std::vector<float> floatSamples(numSamples);
|
|
2098
|
+
|
|
2099
|
+
const int16_t* pcmData = reinterpret_cast<const int16_t*>(audioData.data());
|
|
2100
|
+
for (size_t i = 0; i < numSamples; i++) {
|
|
2101
|
+
floatSamples[i] = static_cast<float>(pcmData[i]) / 32768.0f;
|
|
2102
|
+
}
|
|
2103
|
+
|
|
2104
|
+
LOGI("VAD processing %zu samples", numSamples);
|
|
2105
|
+
|
|
2106
|
+
// Process with VAD
|
|
2107
|
+
rac_bool_t isSpeech = RAC_FALSE;
|
|
2108
|
+
rac_result_t status = rac_vad_component_process(
|
|
2109
|
+
handle,
|
|
2110
|
+
floatSamples.data(),
|
|
2111
|
+
numSamples,
|
|
2112
|
+
&isSpeech
|
|
2113
|
+
);
|
|
2114
|
+
|
|
2115
|
+
if (status != RAC_SUCCESS) {
|
|
2116
|
+
throw std::runtime_error("VAD processing failed with error code: " + std::to_string(status));
|
|
2117
|
+
}
|
|
2118
|
+
|
|
2119
|
+
// Return JSON result
|
|
2120
|
+
std::ostringstream json;
|
|
2121
|
+
json << "{";
|
|
2122
|
+
json << "\"isSpeech\":" << (isSpeech == RAC_TRUE ? "true" : "false") << ",";
|
|
2123
|
+
json << "\"samplesProcessed\":" << numSamples;
|
|
2124
|
+
json << "}";
|
|
2125
|
+
|
|
2126
|
+
return json.str();
|
|
2127
|
+
});
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
std::shared_ptr<Promise<void>> HybridRunAnywhereCore::resetVAD() {
|
|
2131
|
+
return Promise<void>::async([]() -> void {
|
|
2132
|
+
rac_handle_t handle = getGlobalVADHandle();
|
|
2133
|
+
if (handle) {
|
|
2134
|
+
rac_vad_component_reset(handle);
|
|
2135
|
+
}
|
|
2136
|
+
});
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
// ============================================================================
|
|
2140
|
+
// Voice Agent Capability (Backend-Agnostic)
|
|
2141
|
+
// Calls rac_voice_agent_* APIs - requires STT, LLM, TTS, and VAD backends
|
|
2142
|
+
// Uses a global voice agent handle that composes the global component handles
|
|
2143
|
+
// Mirrors Swift SDK's CppBridge.VoiceAgent.shared architecture
|
|
2144
|
+
// ============================================================================
|
|
2145
|
+
|
|
2146
|
+
// Global Voice Agent handle - composes the global STT, LLM, TTS, VAD handles
|
|
2147
|
+
static rac_voice_agent_handle_t g_voice_agent_handle = nullptr;
|
|
2148
|
+
static std::mutex g_voice_agent_mutex;
|
|
2149
|
+
|
|
2150
|
+
static rac_voice_agent_handle_t getGlobalVoiceAgentHandle() {
|
|
2151
|
+
std::lock_guard<std::mutex> lock(g_voice_agent_mutex);
|
|
2152
|
+
if (g_voice_agent_handle == nullptr) {
|
|
2153
|
+
// Get component handles - required for voice agent
|
|
2154
|
+
rac_handle_t llmHandle = getGlobalLLMHandle();
|
|
2155
|
+
rac_handle_t sttHandle = getGlobalSTTHandle();
|
|
2156
|
+
rac_handle_t ttsHandle = getGlobalTTSHandle();
|
|
2157
|
+
rac_handle_t vadHandle = getGlobalVADHandle();
|
|
2158
|
+
|
|
2159
|
+
if (!llmHandle || !sttHandle || !ttsHandle || !vadHandle) {
|
|
2160
|
+
// Cannot create voice agent without all components
|
|
2161
|
+
return nullptr;
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
rac_result_t result = rac_voice_agent_create(
|
|
2165
|
+
llmHandle, sttHandle, ttsHandle, vadHandle, &g_voice_agent_handle);
|
|
2166
|
+
if (result != RAC_SUCCESS) {
|
|
2167
|
+
g_voice_agent_handle = nullptr;
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
return g_voice_agent_handle;
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::initializeVoiceAgent(
|
|
2174
|
+
const std::string& configJson) {
|
|
2175
|
+
return Promise<bool>::async([this, configJson]() -> bool {
|
|
2176
|
+
LOGI("Initializing voice agent...");
|
|
2177
|
+
|
|
2178
|
+
rac_voice_agent_handle_t handle = getGlobalVoiceAgentHandle();
|
|
2179
|
+
if (!handle) {
|
|
2180
|
+
throw std::runtime_error("Voice agent requires STT, LLM, TTS, and VAD backends. "
|
|
2181
|
+
"Install @runanywhere/llamacpp and @runanywhere/onnx.");
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
// Initialize with default config (or parse configJson if needed)
|
|
2185
|
+
rac_result_t result = rac_voice_agent_initialize(handle, nullptr);
|
|
2186
|
+
if (result != RAC_SUCCESS) {
|
|
2187
|
+
throw std::runtime_error("Failed to initialize voice agent: " + std::to_string(result));
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
LOGI("Voice agent initialized");
|
|
2191
|
+
return true;
|
|
2192
|
+
});
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::initializeVoiceAgentWithLoadedModels() {
|
|
2196
|
+
return Promise<bool>::async([this]() -> bool {
|
|
2197
|
+
LOGI("Initializing voice agent with loaded models...");
|
|
2198
|
+
|
|
2199
|
+
rac_voice_agent_handle_t handle = getGlobalVoiceAgentHandle();
|
|
2200
|
+
if (!handle) {
|
|
2201
|
+
throw std::runtime_error("Voice agent requires STT, LLM, TTS, and VAD backends. "
|
|
2202
|
+
"Install @runanywhere/llamacpp and @runanywhere/onnx.");
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
// Initialize using already-loaded models
|
|
2206
|
+
rac_result_t result = rac_voice_agent_initialize_with_loaded_models(handle);
|
|
2207
|
+
if (result != RAC_SUCCESS) {
|
|
2208
|
+
throw std::runtime_error("Voice agent requires all models to be loaded. Error: " + std::to_string(result));
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
LOGI("Voice agent initialized with loaded models");
|
|
2212
|
+
return true;
|
|
2213
|
+
});
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::isVoiceAgentReady() {
|
|
2217
|
+
return Promise<bool>::async([]() -> bool {
|
|
2218
|
+
rac_voice_agent_handle_t handle = getGlobalVoiceAgentHandle();
|
|
2219
|
+
if (!handle) {
|
|
2220
|
+
return false;
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
rac_bool_t isReady = RAC_FALSE;
|
|
2224
|
+
rac_result_t result = rac_voice_agent_is_ready(handle, &isReady);
|
|
2225
|
+
if (result != RAC_SUCCESS) {
|
|
2226
|
+
return false;
|
|
2227
|
+
}
|
|
2228
|
+
|
|
2229
|
+
LOGD("isVoiceAgentReady: %s", isReady == RAC_TRUE ? "true" : "false");
|
|
2230
|
+
return isReady == RAC_TRUE;
|
|
2231
|
+
});
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getVoiceAgentComponentStates() {
|
|
2235
|
+
return Promise<std::string>::async([]() -> std::string {
|
|
2236
|
+
rac_voice_agent_handle_t handle = getGlobalVoiceAgentHandle();
|
|
2237
|
+
|
|
2238
|
+
// Get component loaded states
|
|
2239
|
+
rac_bool_t sttLoaded = RAC_FALSE;
|
|
2240
|
+
rac_bool_t llmLoaded = RAC_FALSE;
|
|
2241
|
+
rac_bool_t ttsLoaded = RAC_FALSE;
|
|
2242
|
+
|
|
2243
|
+
if (handle) {
|
|
2244
|
+
rac_voice_agent_is_stt_loaded(handle, &sttLoaded);
|
|
2245
|
+
rac_voice_agent_is_llm_loaded(handle, &llmLoaded);
|
|
2246
|
+
rac_voice_agent_is_tts_loaded(handle, &ttsLoaded);
|
|
2247
|
+
}
|
|
2248
|
+
|
|
2249
|
+
// Get model IDs if loaded
|
|
2250
|
+
const char* sttModelId = handle ? rac_voice_agent_get_stt_model_id(handle) : nullptr;
|
|
2251
|
+
const char* llmModelId = handle ? rac_voice_agent_get_llm_model_id(handle) : nullptr;
|
|
2252
|
+
const char* ttsVoiceId = handle ? rac_voice_agent_get_tts_voice_id(handle) : nullptr;
|
|
2253
|
+
|
|
2254
|
+
return buildJsonObject({
|
|
2255
|
+
{"stt", buildJsonObject({
|
|
2256
|
+
{"available", handle ? "true" : "false"},
|
|
2257
|
+
{"loaded", sttLoaded == RAC_TRUE ? "true" : "false"},
|
|
2258
|
+
{"modelId", sttModelId ? jsonString(sttModelId) : "null"}
|
|
2259
|
+
})},
|
|
2260
|
+
{"llm", buildJsonObject({
|
|
2261
|
+
{"available", handle ? "true" : "false"},
|
|
2262
|
+
{"loaded", llmLoaded == RAC_TRUE ? "true" : "false"},
|
|
2263
|
+
{"modelId", llmModelId ? jsonString(llmModelId) : "null"}
|
|
2264
|
+
})},
|
|
2265
|
+
{"tts", buildJsonObject({
|
|
2266
|
+
{"available", handle ? "true" : "false"},
|
|
2267
|
+
{"loaded", ttsLoaded == RAC_TRUE ? "true" : "false"},
|
|
2268
|
+
{"voiceId", ttsVoiceId ? jsonString(ttsVoiceId) : "null"}
|
|
2269
|
+
})}
|
|
2270
|
+
});
|
|
2271
|
+
});
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::processVoiceTurn(
|
|
2275
|
+
const std::string& audioBase64) {
|
|
2276
|
+
return Promise<std::string>::async([this, audioBase64]() -> std::string {
|
|
2277
|
+
LOGI("Processing voice turn...");
|
|
2278
|
+
|
|
2279
|
+
rac_voice_agent_handle_t handle = getGlobalVoiceAgentHandle();
|
|
2280
|
+
if (!handle) {
|
|
2281
|
+
throw std::runtime_error("Voice agent not available");
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
// Decode base64 audio
|
|
2285
|
+
std::vector<uint8_t> audioData = base64Decode(audioBase64);
|
|
2286
|
+
if (audioData.empty()) {
|
|
2287
|
+
throw std::runtime_error("Failed to decode audio data");
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
rac_voice_agent_result_t result = {};
|
|
2291
|
+
rac_result_t status = rac_voice_agent_process_voice_turn(
|
|
2292
|
+
handle, audioData.data(), audioData.size(), &result);
|
|
2293
|
+
|
|
2294
|
+
if (status != RAC_SUCCESS) {
|
|
2295
|
+
throw std::runtime_error("Voice turn processing failed: " + std::to_string(status));
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
// Build result JSON
|
|
2299
|
+
std::string responseJson = buildJsonObject({
|
|
2300
|
+
{"speechDetected", result.speech_detected == RAC_TRUE ? "true" : "false"},
|
|
2301
|
+
{"transcription", result.transcription ? jsonString(result.transcription) : "\"\""},
|
|
2302
|
+
{"response", result.response ? jsonString(result.response) : "\"\""},
|
|
2303
|
+
{"audioSize", std::to_string(result.synthesized_audio_size)}
|
|
2304
|
+
});
|
|
2305
|
+
|
|
2306
|
+
// Free result resources
|
|
2307
|
+
rac_voice_agent_result_free(&result);
|
|
2308
|
+
|
|
2309
|
+
LOGI("Voice turn completed");
|
|
2310
|
+
return responseJson;
|
|
2311
|
+
});
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::voiceAgentTranscribe(
|
|
2315
|
+
const std::string& audioBase64) {
|
|
2316
|
+
return Promise<std::string>::async([this, audioBase64]() -> std::string {
|
|
2317
|
+
LOGI("Voice agent transcribing...");
|
|
2318
|
+
|
|
2319
|
+
rac_voice_agent_handle_t handle = getGlobalVoiceAgentHandle();
|
|
2320
|
+
if (!handle) {
|
|
2321
|
+
throw std::runtime_error("Voice agent not available");
|
|
2322
|
+
}
|
|
2323
|
+
|
|
2324
|
+
// Decode base64 audio
|
|
2325
|
+
std::vector<uint8_t> audioData = base64Decode(audioBase64);
|
|
2326
|
+
if (audioData.empty()) {
|
|
2327
|
+
throw std::runtime_error("Failed to decode audio data");
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
char* transcription = nullptr;
|
|
2331
|
+
rac_result_t status = rac_voice_agent_transcribe(
|
|
2332
|
+
handle, audioData.data(), audioData.size(), &transcription);
|
|
2333
|
+
|
|
2334
|
+
if (status != RAC_SUCCESS) {
|
|
2335
|
+
throw std::runtime_error("Transcription failed: " + std::to_string(status));
|
|
2336
|
+
}
|
|
2337
|
+
|
|
2338
|
+
std::string result = transcription ? transcription : "";
|
|
2339
|
+
if (transcription) {
|
|
2340
|
+
free(transcription);
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
return result;
|
|
2344
|
+
});
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::voiceAgentGenerateResponse(
|
|
2348
|
+
const std::string& prompt) {
|
|
2349
|
+
return Promise<std::string>::async([this, prompt]() -> std::string {
|
|
2350
|
+
LOGI("Voice agent generating response...");
|
|
2351
|
+
|
|
2352
|
+
rac_voice_agent_handle_t handle = getGlobalVoiceAgentHandle();
|
|
2353
|
+
if (!handle) {
|
|
2354
|
+
throw std::runtime_error("Voice agent not available");
|
|
2355
|
+
}
|
|
2356
|
+
|
|
2357
|
+
char* response = nullptr;
|
|
2358
|
+
rac_result_t status = rac_voice_agent_generate_response(handle, prompt.c_str(), &response);
|
|
2359
|
+
|
|
2360
|
+
if (status != RAC_SUCCESS) {
|
|
2361
|
+
throw std::runtime_error("Response generation failed: " + std::to_string(status));
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
std::string result = response ? response : "";
|
|
2365
|
+
if (response) {
|
|
2366
|
+
free(response);
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
return result;
|
|
2370
|
+
});
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::voiceAgentSynthesizeSpeech(
|
|
2374
|
+
const std::string& text) {
|
|
2375
|
+
return Promise<std::string>::async([this, text]() -> std::string {
|
|
2376
|
+
LOGI("Voice agent synthesizing speech...");
|
|
2377
|
+
|
|
2378
|
+
rac_voice_agent_handle_t handle = getGlobalVoiceAgentHandle();
|
|
2379
|
+
if (!handle) {
|
|
2380
|
+
throw std::runtime_error("Voice agent not available");
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
void* audioData = nullptr;
|
|
2384
|
+
size_t audioSize = 0;
|
|
2385
|
+
rac_result_t status = rac_voice_agent_synthesize_speech(
|
|
2386
|
+
handle, text.c_str(), &audioData, &audioSize);
|
|
2387
|
+
|
|
2388
|
+
if (status != RAC_SUCCESS) {
|
|
2389
|
+
throw std::runtime_error("Speech synthesis failed: " + std::to_string(status));
|
|
2390
|
+
}
|
|
2391
|
+
|
|
2392
|
+
// Encode audio to base64
|
|
2393
|
+
std::string audioBase64 = base64Encode(static_cast<uint8_t*>(audioData), audioSize);
|
|
2394
|
+
|
|
2395
|
+
if (audioData) {
|
|
2396
|
+
free(audioData);
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
return audioBase64;
|
|
2400
|
+
});
|
|
2401
|
+
}
|
|
2402
|
+
|
|
2403
|
+
std::shared_ptr<Promise<void>> HybridRunAnywhereCore::cleanupVoiceAgent() {
|
|
2404
|
+
return Promise<void>::async([]() -> void {
|
|
2405
|
+
LOGI("Cleaning up voice agent...");
|
|
2406
|
+
|
|
2407
|
+
rac_voice_agent_handle_t handle = getGlobalVoiceAgentHandle();
|
|
2408
|
+
if (handle) {
|
|
2409
|
+
rac_voice_agent_cleanup(handle);
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
// Note: We don't destroy the voice agent handle here - it's reusable
|
|
2413
|
+
// The models can be unloaded separately via unloadSTTModel, etc.
|
|
2414
|
+
});
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
// ============================================================================
|
|
2418
|
+
// Secure Storage Methods
|
|
2419
|
+
// Matches Swift: KeychainManager.swift
|
|
2420
|
+
// ============================================================================
|
|
2421
|
+
|
|
2422
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::secureStorageSet(
|
|
2423
|
+
const std::string& key,
|
|
2424
|
+
const std::string& value) {
|
|
2425
|
+
return Promise<bool>::async([key, value]() -> bool {
|
|
2426
|
+
LOGI("Secure storage set: key=%s", key.c_str());
|
|
2427
|
+
|
|
2428
|
+
bool success = InitBridge::shared().secureSet(key, value);
|
|
2429
|
+
if (!success) {
|
|
2430
|
+
LOGE("Failed to store value for key: %s", key.c_str());
|
|
2431
|
+
}
|
|
2432
|
+
return success;
|
|
2433
|
+
});
|
|
2434
|
+
}
|
|
2435
|
+
|
|
2436
|
+
std::shared_ptr<Promise<std::variant<nitro::NullType, std::string>>> HybridRunAnywhereCore::secureStorageGet(
|
|
2437
|
+
const std::string& key) {
|
|
2438
|
+
return Promise<std::variant<nitro::NullType, std::string>>::async([key]() -> std::variant<nitro::NullType, std::string> {
|
|
2439
|
+
LOGI("Secure storage get: key=%s", key.c_str());
|
|
2440
|
+
|
|
2441
|
+
std::string value;
|
|
2442
|
+
if (InitBridge::shared().secureGet(key, value)) {
|
|
2443
|
+
return value;
|
|
2444
|
+
}
|
|
2445
|
+
return nitro::NullType();
|
|
2446
|
+
});
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::secureStorageDelete(
|
|
2450
|
+
const std::string& key) {
|
|
2451
|
+
return Promise<bool>::async([key]() -> bool {
|
|
2452
|
+
LOGI("Secure storage delete: key=%s", key.c_str());
|
|
2453
|
+
|
|
2454
|
+
bool success = InitBridge::shared().secureDelete(key);
|
|
2455
|
+
if (!success) {
|
|
2456
|
+
LOGE("Failed to delete key: %s", key.c_str());
|
|
2457
|
+
}
|
|
2458
|
+
return success;
|
|
2459
|
+
});
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::secureStorageExists(
|
|
2463
|
+
const std::string& key) {
|
|
2464
|
+
return Promise<bool>::async([key]() -> bool {
|
|
2465
|
+
LOGD("Secure storage exists: key=%s", key.c_str());
|
|
2466
|
+
return InitBridge::shared().secureExists(key);
|
|
2467
|
+
});
|
|
2468
|
+
}
|
|
2469
|
+
|
|
2470
|
+
std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getPersistentDeviceUUID() {
|
|
2471
|
+
return Promise<std::string>::async([]() -> std::string {
|
|
2472
|
+
LOGI("Getting persistent device UUID...");
|
|
2473
|
+
|
|
2474
|
+
std::string uuid = InitBridge::shared().getPersistentDeviceUUID();
|
|
2475
|
+
|
|
2476
|
+
if (uuid.empty()) {
|
|
2477
|
+
throw std::runtime_error("Failed to get or generate device UUID");
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
LOGI("Persistent device UUID: %s", uuid.c_str());
|
|
2481
|
+
return uuid;
|
|
2482
|
+
});
|
|
2483
|
+
}
|
|
2484
|
+
|
|
2485
|
+
// ============================================================================
|
|
2486
|
+
// Telemetry
|
|
2487
|
+
// Matches Swift: CppBridge+Telemetry.swift
|
|
2488
|
+
// C++ handles all telemetry logic - batching, JSON building, routing
|
|
2489
|
+
// ============================================================================
|
|
2490
|
+
|
|
2491
|
+
std::shared_ptr<Promise<void>> HybridRunAnywhereCore::flushTelemetry() {
|
|
2492
|
+
return Promise<void>::async([]() -> void {
|
|
2493
|
+
LOGI("Flushing telemetry events...");
|
|
2494
|
+
TelemetryBridge::shared().flush();
|
|
2495
|
+
LOGI("Telemetry flushed");
|
|
2496
|
+
});
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2499
|
+
std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::isTelemetryInitialized() {
|
|
2500
|
+
return Promise<bool>::async([]() -> bool {
|
|
2501
|
+
return TelemetryBridge::shared().isInitialized();
|
|
2502
|
+
});
|
|
2503
|
+
}
|
|
2504
|
+
|
|
2505
|
+
} // namespace margelo::nitro::runanywhere
|