@runanywhere/core 0.17.8 → 0.18.1

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 (106) hide show
  1. package/README.md +218 -2
  2. package/RunAnywhereCore.podspec +1 -0
  3. package/android/CMakeLists.txt +24 -2
  4. package/android/build.gradle +61 -9
  5. package/android/src/main/cpp/cpp-adapter.cpp +51 -3
  6. package/android/src/main/include/rac/backends/rac_vlm_llamacpp.h +216 -0
  7. package/android/src/main/include/rac/core/capabilities/rac_lifecycle.h +3 -1
  8. package/android/src/main/include/rac/core/rac_core.h +11 -0
  9. package/android/src/main/include/rac/core/rac_types.h +8 -6
  10. package/android/src/main/include/rac/features/diffusion/rac_diffusion.h +22 -0
  11. package/android/src/main/include/rac/features/diffusion/rac_diffusion_component.h +263 -0
  12. package/android/src/main/include/rac/features/diffusion/rac_diffusion_model_registry.h +358 -0
  13. package/android/src/main/include/rac/features/diffusion/rac_diffusion_service.h +187 -0
  14. package/android/src/main/include/rac/features/diffusion/rac_diffusion_tokenizer.h +167 -0
  15. package/android/src/main/include/rac/features/diffusion/rac_diffusion_types.h +454 -0
  16. package/android/src/main/include/rac/features/llm/rac_tool_calling.h +373 -0
  17. package/android/src/main/include/rac/features/platform/rac_diffusion_platform.h +305 -0
  18. package/android/src/main/include/rac/features/vad/rac_vad_energy.h +1 -1
  19. package/android/src/main/include/rac/features/vlm/rac_vlm.h +16 -0
  20. package/android/src/main/include/rac/features/vlm/rac_vlm_component.h +168 -0
  21. package/android/src/main/include/rac/features/vlm/rac_vlm_service.h +206 -0
  22. package/android/src/main/include/rac/features/vlm/rac_vlm_types.h +417 -0
  23. package/android/src/main/include/rac/infrastructure/model_management/rac_model_registry.h +15 -0
  24. package/android/src/main/include/rac/infrastructure/model_management/rac_model_types.h +3 -0
  25. package/android/src/main/include/rac/utils/rac_image_utils.h +215 -0
  26. package/android/src/main/java/com/margelo/nitro/runanywhere/PlatformAdapterBridge.kt +201 -1
  27. package/android/src/main/jniLibs/arm64-v8a/libc++_shared.so +0 -0
  28. package/android/src/main/jniLibs/arm64-v8a/libomp.so +0 -0
  29. package/android/src/main/jniLibs/arm64-v8a/librac_commons.so +0 -0
  30. package/android/src/main/jniLibs/arm64-v8a/librunanywhere_jni.so +0 -0
  31. package/android/src/main/jniLibs/x86_64/libc++_shared.so +0 -0
  32. package/android/src/main/jniLibs/x86_64/libomp.so +0 -0
  33. package/android/src/main/jniLibs/x86_64/librac_commons.so +0 -0
  34. package/android/src/main/jniLibs/x86_64/librunanywhere_jni.so +0 -0
  35. package/cpp/HybridRunAnywhereCore.cpp +263 -163
  36. package/cpp/HybridRunAnywhereCore.hpp +11 -0
  37. package/cpp/bridges/InitBridge.cpp +234 -3
  38. package/cpp/bridges/PlatformDownloadBridge.h +44 -0
  39. package/cpp/bridges/ToolCallingBridge.cpp +188 -0
  40. package/cpp/bridges/ToolCallingBridge.hpp +98 -0
  41. package/cpp/third_party/nlohmann/json.hpp +24765 -0
  42. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/RACommons.h +18 -4
  43. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_core.h +11 -0
  44. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_diffusion.h +22 -0
  45. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_diffusion_component.h +263 -0
  46. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_diffusion_model_registry.h +358 -0
  47. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_diffusion_platform.h +305 -0
  48. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_diffusion_service.h +187 -0
  49. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_diffusion_tokenizer.h +167 -0
  50. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_diffusion_types.h +454 -0
  51. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_endpoints.h +3 -17
  52. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_image_utils.h +215 -0
  53. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_lifecycle.h +3 -1
  54. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_model_assignment.h +4 -20
  55. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_model_registry.h +15 -0
  56. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_model_types.h +3 -0
  57. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_tool_calling.h +373 -0
  58. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_types.h +8 -6
  59. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vad_energy.h +1 -1
  60. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vlm.h +16 -0
  61. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vlm_component.h +168 -0
  62. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vlm_llamacpp.h +216 -0
  63. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vlm_service.h +206 -0
  64. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/Headers/rac_vlm_types.h +417 -0
  65. package/ios/Binaries/RACommons.xcframework/ios-arm64/RACommons.framework/RACommons +0 -0
  66. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/RACommons.h +18 -4
  67. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_core.h +11 -0
  68. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_diffusion.h +22 -0
  69. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_diffusion_component.h +263 -0
  70. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_diffusion_model_registry.h +358 -0
  71. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_diffusion_platform.h +305 -0
  72. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_diffusion_service.h +187 -0
  73. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_diffusion_tokenizer.h +167 -0
  74. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_diffusion_types.h +454 -0
  75. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_endpoints.h +3 -17
  76. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_image_utils.h +215 -0
  77. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_lifecycle.h +3 -1
  78. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_model_assignment.h +4 -20
  79. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_model_registry.h +15 -0
  80. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_model_types.h +3 -0
  81. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_tool_calling.h +373 -0
  82. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_types.h +8 -6
  83. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vad_energy.h +1 -1
  84. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vlm.h +16 -0
  85. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vlm_component.h +168 -0
  86. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vlm_llamacpp.h +216 -0
  87. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vlm_service.h +206 -0
  88. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/Headers/rac_vlm_types.h +417 -0
  89. package/ios/Binaries/RACommons.xcframework/ios-arm64_x86_64-simulator/RACommons.framework/RACommons +0 -0
  90. package/ios/PlatformAdapterBridge.h +24 -1
  91. package/ios/PlatformAdapterBridge.m +243 -0
  92. package/nitrogen/generated/shared/c++/HybridRunAnywhereCoreSpec.cpp +4 -0
  93. package/nitrogen/generated/shared/c++/HybridRunAnywhereCoreSpec.hpp +4 -0
  94. package/package.json +8 -4
  95. package/src/Foundation/Security/SecureStorageService.ts +12 -6
  96. package/src/Public/Extensions/RunAnywhere+Models.ts +5 -3
  97. package/src/Public/Extensions/RunAnywhere+STT.ts +7 -2
  98. package/src/Public/Extensions/RunAnywhere+ToolCalling.ts +472 -0
  99. package/src/Public/Extensions/index.ts +16 -0
  100. package/src/Public/RunAnywhere.ts +18 -0
  101. package/src/index.ts +0 -1
  102. package/src/services/Network/index.ts +0 -1
  103. package/src/services/index.ts +0 -1
  104. package/src/specs/RunAnywhereCore.nitro.ts +72 -0
  105. package/src/types/ToolCallingTypes.ts +198 -0
  106. package/src/types/index.ts +13 -0
@@ -35,6 +35,7 @@
35
35
  #include "bridges/HTTPBridge.hpp"
36
36
  #include "bridges/DownloadBridge.hpp"
37
37
  #include "bridges/TelemetryBridge.hpp"
38
+ #include "bridges/ToolCallingBridge.hpp"
38
39
 
39
40
  // RACommons C API headers for capability methods
40
41
  // These are backend-agnostic - they work with any registered backend
@@ -198,6 +199,7 @@ bool extractBoolValue(const std::string& json, const std::string& key, bool defa
198
199
  rac_inference_framework_t frameworkFromString(const std::string& framework) {
199
200
  if (framework == "LlamaCpp" || framework == "llamacpp") return RAC_FRAMEWORK_LLAMACPP;
200
201
  if (framework == "ONNX" || framework == "onnx") return RAC_FRAMEWORK_ONNX;
202
+ if (framework == "CoreML" || framework == "coreml") return RAC_FRAMEWORK_COREML;
201
203
  if (framework == "FoundationModels") return RAC_FRAMEWORK_FOUNDATION_MODELS;
202
204
  if (framework == "SystemTTS") return RAC_FRAMEWORK_SYSTEM_TTS;
203
205
  return RAC_FRAMEWORK_UNKNOWN;
@@ -421,12 +423,13 @@ std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::initialize(
421
423
  };
422
424
 
423
425
  callbacks.user_data = nullptr;
424
- // Note: auto_fetch was removed from rac_assignment_callbacks struct
425
- // Fetching is now controlled manually via rac_model_assignment_fetch()
426
+ // Only auto-fetch in staging/production, not development
427
+ bool shouldAutoFetch = (env != SDKEnvironment::Development);
428
+ callbacks.auto_fetch = shouldAutoFetch ? RAC_TRUE : RAC_FALSE;
426
429
 
427
430
  result = rac_model_assignment_set_callbacks(&callbacks);
428
431
  if (result == RAC_SUCCESS) {
429
- LOGI("Model assignment callbacks registered");
432
+ LOGI("Model assignment callbacks registered (autoFetch: %s)", shouldAutoFetch ? "true" : "false");
430
433
  } else {
431
434
  LOGE("Failed to register model assignment callbacks: %d", result);
432
435
  // Continue - not fatal, models can be fetched later
@@ -809,6 +812,7 @@ std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getAvailableModels(
809
812
  case RAC_MODEL_CATEGORY_SPEECH_RECOGNITION: categoryStr = "speech-recognition"; break;
810
813
  case RAC_MODEL_CATEGORY_SPEECH_SYNTHESIS: categoryStr = "speech-synthesis"; break;
811
814
  case RAC_MODEL_CATEGORY_VISION: categoryStr = "vision"; break;
815
+ case RAC_MODEL_CATEGORY_IMAGE_GENERATION: categoryStr = "image-generation"; break;
812
816
  case RAC_MODEL_CATEGORY_AUDIO: categoryStr = "audio"; break;
813
817
  case RAC_MODEL_CATEGORY_MULTIMODAL: categoryStr = "multimodal"; break;
814
818
  default: categoryStr = "unknown"; break;
@@ -825,6 +829,7 @@ std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getAvailableModels(
825
829
  switch (m.framework) {
826
830
  case RAC_FRAMEWORK_LLAMACPP: frameworkStr = "LlamaCpp"; break;
827
831
  case RAC_FRAMEWORK_ONNX: frameworkStr = "ONNX"; break;
832
+ case RAC_FRAMEWORK_COREML: frameworkStr = "CoreML"; break;
828
833
  case RAC_FRAMEWORK_FOUNDATION_MODELS: frameworkStr = "FoundationModels"; break;
829
834
  case RAC_FRAMEWORK_SYSTEM_TTS: frameworkStr = "SystemTTS"; break;
830
835
  default: frameworkStr = "unknown"; break;
@@ -887,6 +892,7 @@ std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getModelInfo(
887
892
  switch (m.framework) {
888
893
  case RAC_FRAMEWORK_LLAMACPP: frameworkStr = "LlamaCpp"; break;
889
894
  case RAC_FRAMEWORK_ONNX: frameworkStr = "ONNX"; break;
895
+ case RAC_FRAMEWORK_COREML: frameworkStr = "CoreML"; break;
890
896
  case RAC_FRAMEWORK_FOUNDATION_MODELS: frameworkStr = "FoundationModels"; break;
891
897
  case RAC_FRAMEWORK_SYSTEM_TTS: frameworkStr = "SystemTTS"; break;
892
898
  default: frameworkStr = "unknown"; break;
@@ -1652,21 +1658,38 @@ std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::loadSTTModel(
1652
1658
  const std::string& modelType,
1653
1659
  const std::optional<std::string>& configJson) {
1654
1660
  return Promise<bool>::async([this, modelPath, modelType]() -> bool {
1655
- LOGI("Loading STT model: %s", modelPath.c_str());
1661
+ try {
1662
+ LOGI("Loading STT model: %s", modelPath.c_str());
1656
1663
 
1657
- rac_handle_t handle = getGlobalSTTHandle();
1658
- if (!handle) {
1659
- setLastError("Failed to create STT component. Is an STT backend registered?");
1660
- throw std::runtime_error("STT backend not registered. Install @runanywhere/onnx.");
1661
- }
1664
+ if (modelPath.empty()) {
1665
+ setLastError("STT model path is empty. Download the model first.");
1666
+ return false;
1667
+ }
1662
1668
 
1663
- rac_result_t result = rac_stt_component_load_model(handle, modelPath.c_str(), modelPath.c_str(), modelType.c_str());
1664
- if (result != RAC_SUCCESS) {
1665
- throw std::runtime_error("Failed to load STT model: " + std::to_string(result));
1666
- }
1669
+ rac_handle_t handle = getGlobalSTTHandle();
1670
+ if (!handle) {
1671
+ setLastError("Failed to create STT component. Is an STT backend registered?");
1672
+ return false;
1673
+ }
1667
1674
 
1668
- LOGI("STT model loaded successfully");
1669
- return true;
1675
+ rac_result_t result = rac_stt_component_load_model(
1676
+ handle, modelPath.c_str(), modelPath.c_str(), modelType.c_str());
1677
+ if (result != RAC_SUCCESS) {
1678
+ setLastError("Failed to load STT model: " + std::to_string(result));
1679
+ return false;
1680
+ }
1681
+
1682
+ LOGI("STT model loaded successfully");
1683
+ return true;
1684
+ } catch (const std::exception& e) {
1685
+ std::string msg = e.what();
1686
+ LOGI("loadSTTModel exception: %s", msg.c_str());
1687
+ setLastError(msg);
1688
+ return false;
1689
+ } catch (...) {
1690
+ setLastError("STT model load failed (unknown error)");
1691
+ return false;
1692
+ }
1670
1693
  });
1671
1694
  }
1672
1695
 
@@ -1703,57 +1726,71 @@ std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::transcribe(
1703
1726
  double sampleRate,
1704
1727
  const std::optional<std::string>& language) {
1705
1728
  return Promise<std::string>::async([this, audioBase64, sampleRate, language]() -> std::string {
1706
- LOGI("Transcribing audio (base64)...");
1729
+ try {
1730
+ LOGI("Transcribing audio (base64)...");
1707
1731
 
1708
- rac_handle_t handle = getGlobalSTTHandle();
1709
- if (!handle) {
1710
- throw std::runtime_error("STT component not available. Is an STT backend registered?");
1711
- }
1732
+ rac_handle_t handle = getGlobalSTTHandle();
1733
+ if (!handle) {
1734
+ return "{\"error\":\"STT component not available. Is an STT backend registered?\"}";
1735
+ }
1712
1736
 
1713
- if (rac_stt_component_is_loaded(handle) != RAC_TRUE) {
1714
- throw std::runtime_error("No STT model loaded. Call loadSTTModel first.");
1715
- }
1737
+ if (rac_stt_component_is_loaded(handle) != RAC_TRUE) {
1738
+ return "{\"error\":\"No STT model loaded. Call loadSTTModel first.\"}";
1739
+ }
1716
1740
 
1717
- // Decode base64 audio data
1718
- std::vector<uint8_t> audioData = base64Decode(audioBase64);
1719
- if (audioData.empty()) {
1720
- throw std::runtime_error("Failed to decode base64 audio data");
1721
- }
1741
+ // Decode base64 audio data
1742
+ std::vector<uint8_t> audioData = base64Decode(audioBase64);
1743
+ if (audioData.empty()) {
1744
+ return "{\"error\":\"Failed to decode base64 audio data\"}";
1745
+ }
1722
1746
 
1723
- LOGI("Decoded %zu bytes of audio data", audioData.size());
1747
+ // Minimum ~0.05s at 16kHz 16-bit to avoid backend crash on tiny input
1748
+ if (audioData.size() < 1600) {
1749
+ return "{\"text\":\"\",\"confidence\":0.0}";
1750
+ }
1724
1751
 
1725
- // Set up transcription options
1726
- rac_stt_options_t options = RAC_STT_OPTIONS_DEFAULT;
1727
- options.sample_rate = static_cast<int32_t>(sampleRate > 0 ? sampleRate : 16000);
1728
- options.audio_format = RAC_AUDIO_FORMAT_PCM;
1729
- if (language.has_value() && !language->empty()) {
1730
- options.language = language->c_str();
1731
- }
1752
+ LOGI("Decoded %zu bytes of audio data", audioData.size());
1732
1753
 
1733
- // Transcribe
1734
- rac_stt_result_t result = {};
1735
- rac_result_t status = rac_stt_component_transcribe(
1736
- handle,
1737
- audioData.data(),
1738
- audioData.size(),
1739
- &options,
1740
- &result
1741
- );
1754
+ // Set up transcription options
1755
+ rac_stt_options_t options = RAC_STT_OPTIONS_DEFAULT;
1756
+ options.sample_rate = static_cast<int32_t>(sampleRate > 0 ? sampleRate : 16000);
1757
+ options.audio_format = RAC_AUDIO_FORMAT_PCM;
1758
+ if (language.has_value() && !language->empty()) {
1759
+ options.language = language->c_str();
1760
+ }
1742
1761
 
1743
- if (status != RAC_SUCCESS) {
1744
- throw std::runtime_error("Transcription failed with error code: " + std::to_string(status));
1745
- }
1762
+ // Transcribe
1763
+ rac_stt_result_t result = {};
1764
+ rac_result_t status = rac_stt_component_transcribe(
1765
+ handle,
1766
+ audioData.data(),
1767
+ audioData.size(),
1768
+ &options,
1769
+ &result
1770
+ );
1771
+
1772
+ if (status != RAC_SUCCESS) {
1773
+ rac_stt_result_free(&result);
1774
+ return "{\"error\":\"Transcription failed with error code: " + std::to_string(status) + "\"}";
1775
+ }
1746
1776
 
1747
- std::string transcribedText;
1748
- if (result.text) {
1749
- transcribedText = std::string(result.text);
1750
- }
1777
+ std::string transcribedText;
1778
+ if (result.text) {
1779
+ transcribedText = std::string(result.text);
1780
+ }
1781
+ float confidence = result.confidence;
1751
1782
 
1752
- // Free the result
1753
- rac_stt_result_free(&result);
1783
+ rac_stt_result_free(&result);
1754
1784
 
1755
- LOGI("Transcription result: %s", transcribedText.c_str());
1756
- return transcribedText;
1785
+ LOGI("Transcription result: %s", transcribedText.c_str());
1786
+ return "{\"text\":" + jsonString(transcribedText) + ",\"confidence\":" + std::to_string(confidence) + "}";
1787
+ } catch (const std::exception& e) {
1788
+ std::string msg = e.what();
1789
+ LOGI("Transcribe exception: %s", msg.c_str());
1790
+ return "{\"error\":" + jsonString(msg) + "}";
1791
+ } catch (...) {
1792
+ return "{\"error\":\"Transcription failed (unknown error)\"}";
1793
+ }
1757
1794
  });
1758
1795
  }
1759
1796
 
@@ -1761,137 +1798,133 @@ std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::transcribeFile(
1761
1798
  const std::string& filePath,
1762
1799
  const std::optional<std::string>& language) {
1763
1800
  return Promise<std::string>::async([this, filePath, language]() -> std::string {
1764
- LOGI("Transcribing file: %s", filePath.c_str());
1765
-
1766
- rac_handle_t handle = getGlobalSTTHandle();
1767
- if (!handle) {
1768
- throw std::runtime_error("STT component not available. Is an STT backend registered?");
1769
- }
1801
+ try {
1802
+ LOGI("Transcribing file: %s", filePath.c_str());
1770
1803
 
1771
- if (rac_stt_component_is_loaded(handle) != RAC_TRUE) {
1772
- throw std::runtime_error("No STT model loaded. Call loadSTTModel first.");
1773
- }
1804
+ rac_handle_t handle = getGlobalSTTHandle();
1805
+ if (!handle) {
1806
+ return "{\"error\":\"STT component not available. Is an STT backend registered?\"}";
1807
+ }
1774
1808
 
1775
- // Open the file
1776
- FILE* file = fopen(filePath.c_str(), "rb");
1777
- if (!file) {
1778
- throw std::runtime_error("Failed to open audio file: " + filePath);
1779
- }
1809
+ if (rac_stt_component_is_loaded(handle) != RAC_TRUE) {
1810
+ return "{\"error\":\"No STT model loaded. Call loadSTTModel first.\"}";
1811
+ }
1780
1812
 
1781
- // Get file size
1782
- fseek(file, 0, SEEK_END);
1783
- long fileSize = ftell(file);
1784
- fseek(file, 0, SEEK_SET);
1813
+ // Open the file
1814
+ FILE* file = fopen(filePath.c_str(), "rb");
1815
+ if (!file) {
1816
+ return "{\"error\":\"Failed to open audio file. Check that the path is valid.\"}";
1817
+ }
1785
1818
 
1786
- if (fileSize <= 0) {
1787
- fclose(file);
1788
- throw std::runtime_error("Audio file is empty: " + filePath);
1789
- }
1819
+ // Get file size
1820
+ fseek(file, 0, SEEK_END);
1821
+ long fileSize = ftell(file);
1822
+ fseek(file, 0, SEEK_SET);
1790
1823
 
1791
- LOGI("File size: %ld bytes", fileSize);
1824
+ if (fileSize <= 0) {
1825
+ fclose(file);
1826
+ return "{\"error\":\"Audio file is empty\"}";
1827
+ }
1792
1828
 
1793
- // Read the entire file into memory
1794
- std::vector<uint8_t> fileData(fileSize);
1795
- size_t bytesRead = fread(fileData.data(), 1, fileSize, file);
1796
- fclose(file);
1829
+ LOGI("File size: %ld bytes", fileSize);
1797
1830
 
1798
- if (bytesRead != static_cast<size_t>(fileSize)) {
1799
- throw std::runtime_error("Failed to read audio file completely");
1800
- }
1831
+ // Read the entire file into memory
1832
+ std::vector<uint8_t> fileData(static_cast<size_t>(fileSize));
1833
+ size_t bytesRead = fread(fileData.data(), 1, static_cast<size_t>(fileSize), file);
1834
+ fclose(file);
1801
1835
 
1802
- // Parse WAV header to extract audio data
1803
- // WAV header: RIFF chunk (12 bytes) + fmt chunk + data chunk
1804
- // We need to find the "data" chunk and extract PCM audio
1836
+ if (bytesRead != static_cast<size_t>(fileSize)) {
1837
+ return "{\"error\":\"Failed to read audio file completely\"}";
1838
+ }
1805
1839
 
1806
- const uint8_t* data = fileData.data();
1807
- size_t dataSize = fileData.size();
1808
- int32_t sampleRate = 16000;
1840
+ // Parse WAV header to extract audio data
1841
+ const uint8_t* data = fileData.data();
1842
+ size_t dataSize = fileData.size();
1843
+ int32_t sampleRate = 16000;
1809
1844
 
1810
- // Check RIFF header
1811
- if (dataSize < 44) {
1812
- throw std::runtime_error("File too small to be a valid WAV file");
1813
- }
1845
+ if (dataSize < 44) {
1846
+ return "{\"error\":\"File too small to be a valid WAV file\"}";
1847
+ }
1848
+ if (data[0] != 'R' || data[1] != 'I' || data[2] != 'F' || data[3] != 'F') {
1849
+ return "{\"error\":\"Invalid WAV file: missing RIFF header\"}";
1850
+ }
1851
+ if (data[8] != 'W' || data[9] != 'A' || data[10] != 'V' || data[11] != 'E') {
1852
+ return "{\"error\":\"Invalid WAV file: missing WAVE format\"}";
1853
+ }
1814
1854
 
1815
- // Check "RIFF" signature
1816
- if (data[0] != 'R' || data[1] != 'I' || data[2] != 'F' || data[3] != 'F') {
1817
- throw std::runtime_error("Invalid WAV file: missing RIFF header");
1818
- }
1855
+ size_t pos = 12;
1856
+ size_t audioDataOffset = 0;
1857
+ size_t audioDataSize = 0;
1819
1858
 
1820
- // Check "WAVE" format
1821
- if (data[8] != 'W' || data[9] != 'A' || data[10] != 'V' || data[11] != 'E') {
1822
- throw std::runtime_error("Invalid WAV file: missing WAVE format");
1823
- }
1859
+ while (pos + 8 < dataSize) {
1860
+ char chunkId[5] = {0};
1861
+ memcpy(chunkId, &data[pos], 4);
1862
+ uint32_t chunkSize = *reinterpret_cast<const uint32_t*>(&data[pos + 4]);
1824
1863
 
1825
- // Find "fmt " and "data" chunks
1826
- size_t pos = 12;
1827
- size_t audioDataOffset = 0;
1828
- size_t audioDataSize = 0;
1864
+ if (strcmp(chunkId, "fmt ") == 0) {
1865
+ if (pos + 8 + chunkSize <= dataSize && chunkSize >= 16) {
1866
+ sampleRate = *reinterpret_cast<const int32_t*>(&data[pos + 12]);
1867
+ if (sampleRate <= 0 || sampleRate > 48000) sampleRate = 16000;
1868
+ LOGI("WAV sample rate: %d Hz", sampleRate);
1869
+ }
1870
+ } else if (strcmp(chunkId, "data") == 0) {
1871
+ audioDataOffset = pos + 8;
1872
+ audioDataSize = chunkSize;
1873
+ LOGI("Found audio data: offset=%zu, size=%zu", audioDataOffset, audioDataSize);
1874
+ break;
1875
+ }
1829
1876
 
1830
- while (pos + 8 < dataSize) {
1831
- char chunkId[5] = {0};
1832
- memcpy(chunkId, &data[pos], 4);
1833
- uint32_t chunkSize = *reinterpret_cast<const uint32_t*>(&data[pos + 4]);
1877
+ pos += 8 + chunkSize;
1878
+ if (chunkSize % 2 != 0) pos++;
1879
+ }
1834
1880
 
1835
- if (strcmp(chunkId, "fmt ") == 0) {
1836
- // Parse fmt chunk
1837
- if (pos + 8 + chunkSize <= dataSize && chunkSize >= 16) {
1838
- // Bytes 12-13: Audio format (1 = PCM)
1839
- // Bytes 14-15: Number of channels
1840
- // Bytes 16-19: Sample rate
1841
- sampleRate = *reinterpret_cast<const int32_t*>(&data[pos + 12]);
1842
- LOGI("WAV sample rate: %d Hz", sampleRate);
1843
- }
1844
- } else if (strcmp(chunkId, "data") == 0) {
1845
- // Found data chunk
1846
- audioDataOffset = pos + 8;
1847
- audioDataSize = chunkSize;
1848
- LOGI("Found audio data: offset=%zu, size=%zu", audioDataOffset, audioDataSize);
1849
- break;
1881
+ if (audioDataSize == 0 || audioDataOffset + audioDataSize > dataSize) {
1882
+ return "{\"error\":\"Could not find valid audio data in WAV file\"}";
1850
1883
  }
1851
1884
 
1852
- pos += 8 + chunkSize;
1853
- // Align to 2-byte boundary
1854
- if (chunkSize % 2 != 0) pos++;
1855
- }
1885
+ // Minimum ~0.1s at 16kHz 16-bit; avoid empty or tiny buffers
1886
+ if (audioDataSize < 3200) {
1887
+ return "{\"error\":\"Recording too short to transcribe\"}";
1888
+ }
1856
1889
 
1857
- if (audioDataSize == 0 || audioDataOffset + audioDataSize > dataSize) {
1858
- throw std::runtime_error("Could not find valid audio data in WAV file");
1859
- }
1890
+ rac_stt_options_t options = RAC_STT_OPTIONS_DEFAULT;
1891
+ options.sample_rate = sampleRate;
1892
+ options.audio_format = RAC_AUDIO_FORMAT_PCM;
1893
+ if (language.has_value() && !language->empty()) {
1894
+ options.language = language->c_str();
1895
+ }
1860
1896
 
1861
- // Set up transcription options
1862
- rac_stt_options_t options = RAC_STT_OPTIONS_DEFAULT;
1863
- options.sample_rate = sampleRate;
1864
- options.audio_format = RAC_AUDIO_FORMAT_WAV; // Tell the backend it's WAV format
1865
- if (language.has_value() && !language->empty()) {
1866
- options.language = language->c_str();
1867
- }
1897
+ LOGI("Transcribing %zu bytes of audio at %d Hz", audioDataSize, sampleRate);
1868
1898
 
1869
- LOGI("Transcribing %zu bytes of audio at %d Hz", audioDataSize, sampleRate);
1899
+ rac_stt_result_t result = {};
1900
+ rac_result_t status = rac_stt_component_transcribe(
1901
+ handle,
1902
+ &data[audioDataOffset],
1903
+ audioDataSize,
1904
+ &options,
1905
+ &result
1906
+ );
1870
1907
 
1871
- // Transcribe - pass the raw PCM data (after WAV header)
1872
- rac_stt_result_t result = {};
1873
- rac_result_t status = rac_stt_component_transcribe(
1874
- handle,
1875
- &data[audioDataOffset],
1876
- audioDataSize,
1877
- &options,
1878
- &result
1879
- );
1908
+ if (status != RAC_SUCCESS) {
1909
+ rac_stt_result_free(&result);
1910
+ return "{\"error\":\"Transcription failed with error code: " + std::to_string(status) + "\"}";
1911
+ }
1880
1912
 
1881
- if (status != RAC_SUCCESS) {
1882
- throw std::runtime_error("Transcription failed with error code: " + std::to_string(status));
1883
- }
1913
+ std::string transcribedText;
1914
+ if (result.text) {
1915
+ transcribedText = std::string(result.text);
1916
+ }
1884
1917
 
1885
- std::string transcribedText;
1886
- if (result.text) {
1887
- transcribedText = std::string(result.text);
1918
+ rac_stt_result_free(&result);
1919
+ LOGI("Transcription result: %s", transcribedText.c_str());
1920
+ return transcribedText;
1921
+ } catch (const std::exception& e) {
1922
+ std::string msg = e.what();
1923
+ LOGI("TranscribeFile exception: %s", msg.c_str());
1924
+ return "{\"error\":\"" + msg + "\"}";
1925
+ } catch (...) {
1926
+ return "{\"error\":\"Transcription failed (unknown error)\"}";
1888
1927
  }
1889
-
1890
- // Free the result
1891
- rac_stt_result_free(&result);
1892
-
1893
- LOGI("Transcription result: %s", transcribedText.c_str());
1894
- return transcribedText;
1895
1928
  });
1896
1929
  }
1897
1930
 
@@ -2568,4 +2601,71 @@ std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::isTelemetryInitialized() {
2568
2601
  });
2569
2602
  }
2570
2603
 
2604
+ // ============================================================================
2605
+ // Tool Calling
2606
+ //
2607
+ // ARCHITECTURE:
2608
+ // - C++ (ToolCallingBridge): Parses <tool_call> tags from LLM output.
2609
+ // This is the SINGLE SOURCE OF TRUTH for parsing, ensuring consistency.
2610
+ //
2611
+ // - TypeScript (RunAnywhere+ToolCalling.ts): Handles tool registry, executor
2612
+ // storage, prompt formatting, and orchestration. Executors MUST stay in
2613
+ // TypeScript because they need JavaScript APIs (fetch, device APIs, etc.).
2614
+ //
2615
+ // Only parseToolCallFromOutput is implemented in C++. All other tool calling
2616
+ // functionality (registration, execution, prompt formatting) is in TypeScript.
2617
+ // ============================================================================
2618
+
2619
+ std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::parseToolCallFromOutput(const std::string& llmOutput) {
2620
+ return Promise<std::string>::async([llmOutput]() -> std::string {
2621
+ LOGD("parseToolCallFromOutput: input length=%zu", llmOutput.length());
2622
+
2623
+ // Use ToolCallingBridge for parsing - single source of truth
2624
+ // This ensures consistent <tool_call> tag parsing across all platforms
2625
+ return ::runanywhere::bridges::ToolCallingBridge::shared().parseToolCall(llmOutput);
2626
+ });
2627
+ }
2628
+
2629
+ std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::formatToolsForPrompt(
2630
+ const std::string& toolsJson,
2631
+ const std::string& format
2632
+ ) {
2633
+ return Promise<std::string>::async([toolsJson, format]() -> std::string {
2634
+ LOGD("formatToolsForPrompt: tools length=%zu, format=%s", toolsJson.length(), format.c_str());
2635
+
2636
+ // Use C++ single source of truth for prompt formatting
2637
+ // This eliminates duplicate TypeScript implementation
2638
+ return ::runanywhere::bridges::ToolCallingBridge::shared().formatToolsPrompt(toolsJson, format);
2639
+ });
2640
+ }
2641
+
2642
+ std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::buildInitialPrompt(
2643
+ const std::string& userPrompt,
2644
+ const std::string& toolsJson,
2645
+ const std::string& optionsJson
2646
+ ) {
2647
+ return Promise<std::string>::async([userPrompt, toolsJson, optionsJson]() -> std::string {
2648
+ LOGD("buildInitialPrompt: prompt length=%zu, tools length=%zu", userPrompt.length(), toolsJson.length());
2649
+
2650
+ // Use C++ single source of truth for initial prompt building
2651
+ return ::runanywhere::bridges::ToolCallingBridge::shared().buildInitialPrompt(userPrompt, toolsJson, optionsJson);
2652
+ });
2653
+ }
2654
+
2655
+ std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::buildFollowupPrompt(
2656
+ const std::string& originalPrompt,
2657
+ const std::string& toolsPrompt,
2658
+ const std::string& toolName,
2659
+ const std::string& resultJson,
2660
+ bool keepToolsAvailable
2661
+ ) {
2662
+ return Promise<std::string>::async([originalPrompt, toolsPrompt, toolName, resultJson, keepToolsAvailable]() -> std::string {
2663
+ LOGD("buildFollowupPrompt: tool=%s, keepTools=%d", toolName.c_str(), keepToolsAvailable);
2664
+
2665
+ // Use C++ single source of truth for follow-up prompt building
2666
+ return ::runanywhere::bridges::ToolCallingBridge::shared().buildFollowupPrompt(
2667
+ originalPrompt, toolsPrompt, toolName, resultJson, keepToolsAvailable);
2668
+ });
2669
+ }
2670
+
2571
2671
  } // namespace margelo::nitro::runanywhere
@@ -257,6 +257,17 @@ public:
257
257
  std::shared_ptr<Promise<std::string>> voiceAgentSynthesizeSpeech(const std::string& text) override;
258
258
  std::shared_ptr<Promise<void>> cleanupVoiceAgent() override;
259
259
 
260
+ // ============================================================================
261
+ // Tool Calling - Delegates to ToolCallingBridge
262
+ // Single source of truth for parsing, formatting, and prompt building
263
+ // Tool registry and execution are in TypeScript (RunAnywhere+ToolCalling.ts)
264
+ // ============================================================================
265
+
266
+ std::shared_ptr<Promise<std::string>> parseToolCallFromOutput(const std::string& llmOutput) override;
267
+ std::shared_ptr<Promise<std::string>> formatToolsForPrompt(const std::string& toolsJson, const std::string& format) override;
268
+ std::shared_ptr<Promise<std::string>> buildInitialPrompt(const std::string& userPrompt, const std::string& toolsJson, const std::string& optionsJson) override;
269
+ std::shared_ptr<Promise<std::string>> buildFollowupPrompt(const std::string& originalPrompt, const std::string& toolsPrompt, const std::string& toolName, const std::string& resultJson, bool keepToolsAvailable) override;
270
+
260
271
  private:
261
272
  // Thread safety
262
273
  std::mutex initMutex_;