@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.
Files changed (354) hide show
  1. package/RunAnywhereCore.podspec +130 -0
  2. package/android/CMakeLists.txt +92 -0
  3. package/android/build.gradle +321 -0
  4. package/android/consumer-rules.pro +5 -0
  5. package/android/src/main/AndroidManifest.xml +3 -0
  6. package/android/src/main/cpp/cpp-adapter.cpp +271 -0
  7. package/android/src/main/include/rac/backends/rac_llm_llamacpp.h +218 -0
  8. package/android/src/main/include/rac/backends/rac_stt_onnx.h +99 -0
  9. package/android/src/main/include/rac/backends/rac_stt_whispercpp.h +153 -0
  10. package/android/src/main/include/rac/backends/rac_tts_onnx.h +71 -0
  11. package/android/src/main/include/rac/backends/rac_vad_onnx.h +84 -0
  12. package/android/src/main/include/rac/core/capabilities/rac_lifecycle.h +290 -0
  13. package/android/src/main/include/rac/core/rac_analytics_events.h +610 -0
  14. package/android/src/main/include/rac/core/rac_audio_utils.h +88 -0
  15. package/android/src/main/include/rac/core/rac_component_types.h +160 -0
  16. package/android/src/main/include/rac/core/rac_core.h +331 -0
  17. package/android/src/main/include/rac/core/rac_error.h +469 -0
  18. package/android/src/main/include/rac/core/rac_events.h +334 -0
  19. package/android/src/main/include/rac/core/rac_logger.h +416 -0
  20. package/android/src/main/include/rac/core/rac_platform_adapter.h +340 -0
  21. package/android/src/main/include/rac/core/rac_sdk_state.h +292 -0
  22. package/android/src/main/include/rac/core/rac_structured_error.h +594 -0
  23. package/android/src/main/include/rac/core/rac_types.h +264 -0
  24. package/android/src/main/include/rac/features/llm/rac_llm.h +17 -0
  25. package/android/src/main/include/rac/features/llm/rac_llm_analytics.h +188 -0
  26. package/android/src/main/include/rac/features/llm/rac_llm_component.h +228 -0
  27. package/android/src/main/include/rac/features/llm/rac_llm_events.h +215 -0
  28. package/android/src/main/include/rac/features/llm/rac_llm_metrics.h +402 -0
  29. package/android/src/main/include/rac/features/llm/rac_llm_service.h +163 -0
  30. package/android/src/main/include/rac/features/llm/rac_llm_structured_output.h +141 -0
  31. package/android/src/main/include/rac/features/llm/rac_llm_types.h +384 -0
  32. package/android/src/main/include/rac/features/platform/rac_llm_platform.h +204 -0
  33. package/android/src/main/include/rac/features/platform/rac_tts_platform.h +197 -0
  34. package/android/src/main/include/rac/features/stt/rac_stt.h +17 -0
  35. package/android/src/main/include/rac/features/stt/rac_stt_analytics.h +204 -0
  36. package/android/src/main/include/rac/features/stt/rac_stt_component.h +162 -0
  37. package/android/src/main/include/rac/features/stt/rac_stt_events.h +62 -0
  38. package/android/src/main/include/rac/features/stt/rac_stt_service.h +154 -0
  39. package/android/src/main/include/rac/features/stt/rac_stt_types.h +389 -0
  40. package/android/src/main/include/rac/features/tts/rac_tts.h +17 -0
  41. package/android/src/main/include/rac/features/tts/rac_tts_analytics.h +181 -0
  42. package/android/src/main/include/rac/features/tts/rac_tts_component.h +158 -0
  43. package/android/src/main/include/rac/features/tts/rac_tts_events.h +54 -0
  44. package/android/src/main/include/rac/features/tts/rac_tts_service.h +162 -0
  45. package/android/src/main/include/rac/features/tts/rac_tts_types.h +374 -0
  46. package/android/src/main/include/rac/features/vad/rac_vad.h +17 -0
  47. package/android/src/main/include/rac/features/vad/rac_vad_analytics.h +236 -0
  48. package/android/src/main/include/rac/features/vad/rac_vad_component.h +185 -0
  49. package/android/src/main/include/rac/features/vad/rac_vad_energy.h +443 -0
  50. package/android/src/main/include/rac/features/vad/rac_vad_events.h +76 -0
  51. package/android/src/main/include/rac/features/vad/rac_vad_service.h +167 -0
  52. package/android/src/main/include/rac/features/vad/rac_vad_types.h +244 -0
  53. package/android/src/main/include/rac/features/voice_agent/rac_voice_agent.h +612 -0
  54. package/android/src/main/include/rac/infrastructure/device/rac_device_manager.h +176 -0
  55. package/android/src/main/include/rac/infrastructure/download/rac_download.h +418 -0
  56. package/android/src/main/include/rac/infrastructure/events/rac_events.h +177 -0
  57. package/android/src/main/include/rac/infrastructure/model_management/rac_model_assignment.h +169 -0
  58. package/android/src/main/include/rac/infrastructure/model_management/rac_model_paths.h +258 -0
  59. package/android/src/main/include/rac/infrastructure/model_management/rac_model_registry.h +357 -0
  60. package/android/src/main/include/rac/infrastructure/model_management/rac_model_strategy.h +374 -0
  61. package/android/src/main/include/rac/infrastructure/model_management/rac_model_types.h +613 -0
  62. package/android/src/main/include/rac/infrastructure/network/rac_api_types.h +335 -0
  63. package/android/src/main/include/rac/infrastructure/network/rac_auth_manager.h +252 -0
  64. package/android/src/main/include/rac/infrastructure/network/rac_dev_config.h +85 -0
  65. package/android/src/main/include/rac/infrastructure/network/rac_endpoints.h +102 -0
  66. package/android/src/main/include/rac/infrastructure/network/rac_environment.h +220 -0
  67. package/android/src/main/include/rac/infrastructure/network/rac_http_client.h +233 -0
  68. package/android/src/main/include/rac/infrastructure/storage/rac_storage_analyzer.h +286 -0
  69. package/android/src/main/include/rac/infrastructure/telemetry/rac_telemetry_manager.h +206 -0
  70. package/android/src/main/include/rac/infrastructure/telemetry/rac_telemetry_types.h +234 -0
  71. package/android/src/main/java/com/margelo/nitro/runanywhere/ArchiveUtility.kt +308 -0
  72. package/android/src/main/java/com/margelo/nitro/runanywhere/HybridRunAnywhereDeviceInfo.kt +229 -0
  73. package/android/src/main/java/com/margelo/nitro/runanywhere/PlatformAdapterBridge.kt +392 -0
  74. package/android/src/main/java/com/margelo/nitro/runanywhere/RunAnywhereCorePackage.kt +28 -0
  75. package/android/src/main/java/com/margelo/nitro/runanywhere/SDKLogger.kt +357 -0
  76. package/android/src/main/java/com/margelo/nitro/runanywhere/SecureStorageManager.kt +147 -0
  77. package/android/src/main/jniLibs/arm64-v8a/libc++_shared.so +0 -0
  78. package/android/src/main/jniLibs/arm64-v8a/libomp.so +0 -0
  79. package/android/src/main/jniLibs/arm64-v8a/librac_commons.so +0 -0
  80. package/android/src/main/jniLibs/arm64-v8a/librunanywhere_jni.so +0 -0
  81. package/cpp/HybridRunAnywhereCore.cpp +2505 -0
  82. package/cpp/HybridRunAnywhereCore.hpp +271 -0
  83. package/cpp/bridges/AuthBridge.cpp +209 -0
  84. package/cpp/bridges/AuthBridge.hpp +157 -0
  85. package/cpp/bridges/DeviceBridge.cpp +257 -0
  86. package/cpp/bridges/DeviceBridge.hpp +155 -0
  87. package/cpp/bridges/DownloadBridge.cpp +299 -0
  88. package/cpp/bridges/DownloadBridge.hpp +197 -0
  89. package/cpp/bridges/EventBridge.cpp +125 -0
  90. package/cpp/bridges/EventBridge.hpp +139 -0
  91. package/cpp/bridges/HTTPBridge.cpp +96 -0
  92. package/cpp/bridges/HTTPBridge.hpp +144 -0
  93. package/cpp/bridges/InitBridge.cpp +1273 -0
  94. package/cpp/bridges/InitBridge.hpp +306 -0
  95. package/cpp/bridges/ModelRegistryBridge.cpp +394 -0
  96. package/cpp/bridges/ModelRegistryBridge.hpp +177 -0
  97. package/cpp/bridges/StorageBridge.cpp +269 -0
  98. package/cpp/bridges/StorageBridge.hpp +172 -0
  99. package/cpp/bridges/TelemetryBridge.cpp +352 -0
  100. package/cpp/bridges/TelemetryBridge.hpp +126 -0
  101. package/ios/.testlocal +0 -0
  102. package/ios/ArchiveUtility.swift +526 -0
  103. package/ios/ArchiveUtilityBridge.m +52 -0
  104. package/ios/AudioDecoder.h +38 -0
  105. package/ios/AudioDecoder.m +162 -0
  106. package/ios/Binaries/RACommons.xcframework/Info.plist +44 -0
  107. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/RACommons.h +67 -0
  108. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_analytics_events.h +610 -0
  109. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_api_types.h +335 -0
  110. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_audio_utils.h +88 -0
  111. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_auth_manager.h +252 -0
  112. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_component_types.h +160 -0
  113. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_core.h +331 -0
  114. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_dev_config.h +85 -0
  115. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_device_manager.h +176 -0
  116. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_download.h +418 -0
  117. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_endpoints.h +102 -0
  118. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_environment.h +220 -0
  119. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_error.h +469 -0
  120. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_events.h +177 -0
  121. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_http_client.h +233 -0
  122. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_lifecycle.h +290 -0
  123. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm.h +17 -0
  124. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_analytics.h +188 -0
  125. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_component.h +228 -0
  126. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_events.h +215 -0
  127. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_llamacpp.h +218 -0
  128. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_metrics.h +402 -0
  129. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_platform.h +204 -0
  130. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_service.h +163 -0
  131. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_structured_output.h +141 -0
  132. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_llm_types.h +384 -0
  133. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_logger.h +416 -0
  134. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_model_assignment.h +169 -0
  135. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_model_paths.h +258 -0
  136. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_model_registry.h +357 -0
  137. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_model_strategy.h +374 -0
  138. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_model_types.h +613 -0
  139. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_platform_adapter.h +340 -0
  140. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_sdk_state.h +292 -0
  141. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_storage_analyzer.h +286 -0
  142. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_structured_error.h +594 -0
  143. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_stt.h +17 -0
  144. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_stt_analytics.h +204 -0
  145. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_stt_component.h +162 -0
  146. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_stt_events.h +62 -0
  147. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_stt_onnx.h +99 -0
  148. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_stt_service.h +154 -0
  149. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_stt_types.h +389 -0
  150. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_stt_whispercpp.h +153 -0
  151. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_telemetry_manager.h +206 -0
  152. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_telemetry_types.h +234 -0
  153. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tts.h +17 -0
  154. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tts_analytics.h +181 -0
  155. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tts_component.h +158 -0
  156. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tts_events.h +54 -0
  157. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tts_onnx.h +71 -0
  158. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tts_platform.h +197 -0
  159. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tts_service.h +162 -0
  160. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tts_types.h +374 -0
  161. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_types.h +264 -0
  162. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad.h +17 -0
  163. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad_analytics.h +236 -0
  164. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad_component.h +185 -0
  165. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad_energy.h +443 -0
  166. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad_events.h +76 -0
  167. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad_onnx.h +84 -0
  168. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad_service.h +167 -0
  169. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad_types.h +244 -0
  170. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_voice_agent.h +612 -0
  171. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Info.plist +11 -0
  172. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Modules/module.modulemap +5 -0
  173. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/RACommons +0 -0
  174. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/RACommons.h +67 -0
  175. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_analytics_events.h +610 -0
  176. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_api_types.h +335 -0
  177. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_audio_utils.h +88 -0
  178. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_auth_manager.h +252 -0
  179. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_component_types.h +160 -0
  180. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_core.h +331 -0
  181. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_dev_config.h +85 -0
  182. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_device_manager.h +176 -0
  183. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_download.h +418 -0
  184. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_endpoints.h +102 -0
  185. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_environment.h +220 -0
  186. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_error.h +469 -0
  187. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_events.h +177 -0
  188. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_http_client.h +233 -0
  189. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_lifecycle.h +290 -0
  190. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm.h +17 -0
  191. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_analytics.h +188 -0
  192. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_component.h +228 -0
  193. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_events.h +215 -0
  194. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_llamacpp.h +218 -0
  195. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_metrics.h +402 -0
  196. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_platform.h +204 -0
  197. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_service.h +163 -0
  198. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_structured_output.h +141 -0
  199. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_llm_types.h +384 -0
  200. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_logger.h +416 -0
  201. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_model_assignment.h +169 -0
  202. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_model_paths.h +258 -0
  203. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_model_registry.h +357 -0
  204. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_model_strategy.h +374 -0
  205. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_model_types.h +613 -0
  206. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_platform_adapter.h +340 -0
  207. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_sdk_state.h +292 -0
  208. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_storage_analyzer.h +286 -0
  209. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_structured_error.h +594 -0
  210. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_stt.h +17 -0
  211. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_stt_analytics.h +204 -0
  212. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_stt_component.h +162 -0
  213. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_stt_events.h +62 -0
  214. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_stt_onnx.h +99 -0
  215. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_stt_service.h +154 -0
  216. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_stt_types.h +389 -0
  217. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_stt_whispercpp.h +153 -0
  218. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_telemetry_manager.h +206 -0
  219. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_telemetry_types.h +234 -0
  220. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tts.h +17 -0
  221. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tts_analytics.h +181 -0
  222. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tts_component.h +158 -0
  223. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tts_events.h +54 -0
  224. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tts_onnx.h +71 -0
  225. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tts_platform.h +197 -0
  226. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tts_service.h +162 -0
  227. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tts_types.h +374 -0
  228. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_types.h +264 -0
  229. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad.h +17 -0
  230. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad_analytics.h +236 -0
  231. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad_component.h +185 -0
  232. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad_energy.h +443 -0
  233. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad_events.h +76 -0
  234. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad_onnx.h +84 -0
  235. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad_service.h +167 -0
  236. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad_types.h +244 -0
  237. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_voice_agent.h +612 -0
  238. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Info.plist +11 -0
  239. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Modules/module.modulemap +5 -0
  240. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/RACommons +0 -0
  241. package/ios/HybridRunAnywhereDeviceInfo.swift +214 -0
  242. package/ios/KeychainManager.swift +116 -0
  243. package/ios/PlatformAdapter.swift +100 -0
  244. package/ios/PlatformAdapterBridge.h +152 -0
  245. package/ios/PlatformAdapterBridge.m +570 -0
  246. package/ios/RNSDKLoggerBridge.h +41 -0
  247. package/ios/RNSDKLoggerBridge.m +66 -0
  248. package/ios/SDKLogger.swift +329 -0
  249. package/nitro.json +20 -0
  250. package/nitrogen/generated/.gitattributes +1 -0
  251. package/nitrogen/generated/android/c++/JHybridRunAnywhereDeviceInfoSpec.cpp +257 -0
  252. package/nitrogen/generated/android/c++/JHybridRunAnywhereDeviceInfoSpec.hpp +77 -0
  253. package/nitrogen/generated/android/kotlin/com/margelo/nitro/runanywhere/HybridRunAnywhereDeviceInfoSpec.kt +106 -0
  254. package/nitrogen/generated/android/kotlin/com/margelo/nitro/runanywhere/runanywherecoreOnLoad.kt +35 -0
  255. package/nitrogen/generated/android/runanywherecore+autolinking.cmake +82 -0
  256. package/nitrogen/generated/android/runanywherecore+autolinking.gradle +27 -0
  257. package/nitrogen/generated/android/runanywherecoreOnLoad.cpp +54 -0
  258. package/nitrogen/generated/android/runanywherecoreOnLoad.hpp +25 -0
  259. package/nitrogen/generated/ios/RunAnywhereCore+autolinking.rb +60 -0
  260. package/nitrogen/generated/ios/RunAnywhereCore-Swift-Cxx-Bridge.cpp +65 -0
  261. package/nitrogen/generated/ios/RunAnywhereCore-Swift-Cxx-Bridge.hpp +197 -0
  262. package/nitrogen/generated/ios/RunAnywhereCore-Swift-Cxx-Umbrella.hpp +45 -0
  263. package/nitrogen/generated/ios/RunAnywhereCoreAutolinking.mm +43 -0
  264. package/nitrogen/generated/ios/RunAnywhereCoreAutolinking.swift +25 -0
  265. package/nitrogen/generated/ios/c++/HybridRunAnywhereDeviceInfoSpecSwift.cpp +11 -0
  266. package/nitrogen/generated/ios/c++/HybridRunAnywhereDeviceInfoSpecSwift.hpp +173 -0
  267. package/nitrogen/generated/ios/swift/Func_void_bool.swift +47 -0
  268. package/nitrogen/generated/ios/swift/Func_void_double.swift +47 -0
  269. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
  270. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
  271. package/nitrogen/generated/ios/swift/HybridRunAnywhereDeviceInfoSpec.swift +68 -0
  272. package/nitrogen/generated/ios/swift/HybridRunAnywhereDeviceInfoSpec_cxx.swift +366 -0
  273. package/nitrogen/generated/shared/c++/HybridRunAnywhereCoreSpec.cpp +92 -0
  274. package/nitrogen/generated/shared/c++/HybridRunAnywhereCoreSpec.hpp +138 -0
  275. package/nitrogen/generated/shared/c++/HybridRunAnywhereDeviceInfoSpec.cpp +33 -0
  276. package/nitrogen/generated/shared/c++/HybridRunAnywhereDeviceInfoSpec.hpp +75 -0
  277. package/package.json +76 -0
  278. package/react-native.config.js +14 -0
  279. package/src/Features/VoiceSession/AudioCaptureManager.ts +286 -0
  280. package/src/Features/VoiceSession/AudioPlaybackManager.ts +300 -0
  281. package/src/Features/VoiceSession/VoiceSessionHandle.ts +530 -0
  282. package/src/Features/VoiceSession/index.ts +20 -0
  283. package/src/Features/index.ts +7 -0
  284. package/src/Foundation/Constants/SDKConstants.ts +47 -0
  285. package/src/Foundation/Constants/index.ts +8 -0
  286. package/src/Foundation/DependencyInjection/ServiceContainer.ts +154 -0
  287. package/src/Foundation/DependencyInjection/ServiceRegistry.ts +51 -0
  288. package/src/Foundation/DependencyInjection/index.ts +6 -0
  289. package/src/Foundation/ErrorTypes/ErrorCategory.ts +184 -0
  290. package/src/Foundation/ErrorTypes/ErrorCodes.ts +151 -0
  291. package/src/Foundation/ErrorTypes/ErrorContext.ts +201 -0
  292. package/src/Foundation/ErrorTypes/SDKError.ts +507 -0
  293. package/src/Foundation/ErrorTypes/index.ts +57 -0
  294. package/src/Foundation/Initialization/InitializationPhase.ts +85 -0
  295. package/src/Foundation/Initialization/InitializationState.ts +168 -0
  296. package/src/Foundation/Initialization/index.ts +26 -0
  297. package/src/Foundation/Logging/Destinations/NativeLogBridge.ts +147 -0
  298. package/src/Foundation/Logging/Destinations/SentryDestination.ts +209 -0
  299. package/src/Foundation/Logging/Logger/SDKLogger.ts +232 -0
  300. package/src/Foundation/Logging/Models/LogLevel.ts +36 -0
  301. package/src/Foundation/Logging/Models/LoggingConfiguration.ts +117 -0
  302. package/src/Foundation/Logging/Services/LoggingManager.ts +407 -0
  303. package/src/Foundation/Logging/index.ts +59 -0
  304. package/src/Foundation/Security/DeviceIdentity.ts +92 -0
  305. package/src/Foundation/Security/SecureStorageError.ts +132 -0
  306. package/src/Foundation/Security/SecureStorageKeys.ts +35 -0
  307. package/src/Foundation/Security/SecureStorageService.ts +449 -0
  308. package/src/Foundation/Security/index.ts +17 -0
  309. package/src/Foundation/index.ts +26 -0
  310. package/src/Infrastructure/Events/EventPublisher.ts +165 -0
  311. package/src/Infrastructure/Events/SDKEvent.ts +214 -0
  312. package/src/Infrastructure/Events/index.ts +15 -0
  313. package/src/Infrastructure/index.ts +9 -0
  314. package/src/Public/Events/EventBus.ts +488 -0
  315. package/src/Public/Events/index.ts +8 -0
  316. package/src/Public/Extensions/RunAnywhere+Logging.ts +51 -0
  317. package/src/Public/Extensions/RunAnywhere+Models.ts +392 -0
  318. package/src/Public/Extensions/RunAnywhere+STT.ts +424 -0
  319. package/src/Public/Extensions/RunAnywhere+Storage.ts +151 -0
  320. package/src/Public/Extensions/RunAnywhere+StructuredOutput.ts +316 -0
  321. package/src/Public/Extensions/RunAnywhere+TTS.ts +430 -0
  322. package/src/Public/Extensions/RunAnywhere+TextGeneration.ts +320 -0
  323. package/src/Public/Extensions/RunAnywhere+VAD.ts +359 -0
  324. package/src/Public/Extensions/RunAnywhere+VoiceAgent.ts +225 -0
  325. package/src/Public/Extensions/RunAnywhere+VoiceSession.ts +155 -0
  326. package/src/Public/Extensions/index.ts +126 -0
  327. package/src/Public/RunAnywhere.ts +695 -0
  328. package/src/index.ts +238 -0
  329. package/src/native/NativeRunAnywhereCore.ts +291 -0
  330. package/src/native/NativeRunAnywhereModule.ts +32 -0
  331. package/src/native/index.ts +21 -0
  332. package/src/services/DownloadService.ts +282 -0
  333. package/src/services/FileSystem.ts +810 -0
  334. package/src/services/ModelRegistry.ts +246 -0
  335. package/src/services/Network/APIEndpoints.ts +84 -0
  336. package/src/services/Network/HTTPService.ts +479 -0
  337. package/src/services/Network/NetworkConfiguration.ts +130 -0
  338. package/src/services/Network/TelemetryService.ts +318 -0
  339. package/src/services/Network/index.ts +29 -0
  340. package/src/services/SystemTTSService.ts +130 -0
  341. package/src/services/index.ts +66 -0
  342. package/src/specs/RunAnywhereCore.nitro.ts +627 -0
  343. package/src/specs/RunAnywhereDeviceInfo.nitro.ts +73 -0
  344. package/src/types/LLMTypes.ts +127 -0
  345. package/src/types/STTTypes.ts +124 -0
  346. package/src/types/StructuredOutputTypes.ts +156 -0
  347. package/src/types/TTSTypes.ts +126 -0
  348. package/src/types/VADTypes.ts +70 -0
  349. package/src/types/VoiceAgentTypes.ts +182 -0
  350. package/src/types/enums.ts +258 -0
  351. package/src/types/events.ts +337 -0
  352. package/src/types/external.d.ts +142 -0
  353. package/src/types/index.ts +146 -0
  354. 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