@runanywhere/core 0.17.7 → 0.18.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 (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 +259 -160
  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;
@@ -810,6 +812,7 @@ std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getAvailableModels(
810
812
  case RAC_MODEL_CATEGORY_SPEECH_RECOGNITION: categoryStr = "speech-recognition"; break;
811
813
  case RAC_MODEL_CATEGORY_SPEECH_SYNTHESIS: categoryStr = "speech-synthesis"; break;
812
814
  case RAC_MODEL_CATEGORY_VISION: categoryStr = "vision"; break;
815
+ case RAC_MODEL_CATEGORY_IMAGE_GENERATION: categoryStr = "image-generation"; break;
813
816
  case RAC_MODEL_CATEGORY_AUDIO: categoryStr = "audio"; break;
814
817
  case RAC_MODEL_CATEGORY_MULTIMODAL: categoryStr = "multimodal"; break;
815
818
  default: categoryStr = "unknown"; break;
@@ -826,6 +829,7 @@ std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getAvailableModels(
826
829
  switch (m.framework) {
827
830
  case RAC_FRAMEWORK_LLAMACPP: frameworkStr = "LlamaCpp"; break;
828
831
  case RAC_FRAMEWORK_ONNX: frameworkStr = "ONNX"; break;
832
+ case RAC_FRAMEWORK_COREML: frameworkStr = "CoreML"; break;
829
833
  case RAC_FRAMEWORK_FOUNDATION_MODELS: frameworkStr = "FoundationModels"; break;
830
834
  case RAC_FRAMEWORK_SYSTEM_TTS: frameworkStr = "SystemTTS"; break;
831
835
  default: frameworkStr = "unknown"; break;
@@ -888,6 +892,7 @@ std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::getModelInfo(
888
892
  switch (m.framework) {
889
893
  case RAC_FRAMEWORK_LLAMACPP: frameworkStr = "LlamaCpp"; break;
890
894
  case RAC_FRAMEWORK_ONNX: frameworkStr = "ONNX"; break;
895
+ case RAC_FRAMEWORK_COREML: frameworkStr = "CoreML"; break;
891
896
  case RAC_FRAMEWORK_FOUNDATION_MODELS: frameworkStr = "FoundationModels"; break;
892
897
  case RAC_FRAMEWORK_SYSTEM_TTS: frameworkStr = "SystemTTS"; break;
893
898
  default: frameworkStr = "unknown"; break;
@@ -1653,21 +1658,38 @@ std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::loadSTTModel(
1653
1658
  const std::string& modelType,
1654
1659
  const std::optional<std::string>& configJson) {
1655
1660
  return Promise<bool>::async([this, modelPath, modelType]() -> bool {
1656
- LOGI("Loading STT model: %s", modelPath.c_str());
1661
+ try {
1662
+ LOGI("Loading STT model: %s", modelPath.c_str());
1657
1663
 
1658
- rac_handle_t handle = getGlobalSTTHandle();
1659
- if (!handle) {
1660
- setLastError("Failed to create STT component. Is an STT backend registered?");
1661
- throw std::runtime_error("STT backend not registered. Install @runanywhere/onnx.");
1662
- }
1664
+ if (modelPath.empty()) {
1665
+ setLastError("STT model path is empty. Download the model first.");
1666
+ return false;
1667
+ }
1663
1668
 
1664
- rac_result_t result = rac_stt_component_load_model(handle, modelPath.c_str(), modelPath.c_str(), modelType.c_str());
1665
- if (result != RAC_SUCCESS) {
1666
- throw std::runtime_error("Failed to load STT model: " + std::to_string(result));
1667
- }
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
+ }
1668
1674
 
1669
- LOGI("STT model loaded successfully");
1670
- 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
+ }
1671
1693
  });
1672
1694
  }
1673
1695
 
@@ -1704,57 +1726,71 @@ std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::transcribe(
1704
1726
  double sampleRate,
1705
1727
  const std::optional<std::string>& language) {
1706
1728
  return Promise<std::string>::async([this, audioBase64, sampleRate, language]() -> std::string {
1707
- LOGI("Transcribing audio (base64)...");
1729
+ try {
1730
+ LOGI("Transcribing audio (base64)...");
1708
1731
 
1709
- rac_handle_t handle = getGlobalSTTHandle();
1710
- if (!handle) {
1711
- throw std::runtime_error("STT component not available. Is an STT backend registered?");
1712
- }
1732
+ rac_handle_t handle = getGlobalSTTHandle();
1733
+ if (!handle) {
1734
+ return "{\"error\":\"STT component not available. Is an STT backend registered?\"}";
1735
+ }
1713
1736
 
1714
- if (rac_stt_component_is_loaded(handle) != RAC_TRUE) {
1715
- throw std::runtime_error("No STT model loaded. Call loadSTTModel first.");
1716
- }
1737
+ if (rac_stt_component_is_loaded(handle) != RAC_TRUE) {
1738
+ return "{\"error\":\"No STT model loaded. Call loadSTTModel first.\"}";
1739
+ }
1717
1740
 
1718
- // Decode base64 audio data
1719
- std::vector<uint8_t> audioData = base64Decode(audioBase64);
1720
- if (audioData.empty()) {
1721
- throw std::runtime_error("Failed to decode base64 audio data");
1722
- }
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
+ }
1723
1746
 
1724
- 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
+ }
1725
1751
 
1726
- // Set up transcription options
1727
- rac_stt_options_t options = RAC_STT_OPTIONS_DEFAULT;
1728
- options.sample_rate = static_cast<int32_t>(sampleRate > 0 ? sampleRate : 16000);
1729
- options.audio_format = RAC_AUDIO_FORMAT_PCM;
1730
- if (language.has_value() && !language->empty()) {
1731
- options.language = language->c_str();
1732
- }
1752
+ LOGI("Decoded %zu bytes of audio data", audioData.size());
1733
1753
 
1734
- // Transcribe
1735
- rac_stt_result_t result = {};
1736
- rac_result_t status = rac_stt_component_transcribe(
1737
- handle,
1738
- audioData.data(),
1739
- audioData.size(),
1740
- &options,
1741
- &result
1742
- );
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
+ }
1743
1761
 
1744
- if (status != RAC_SUCCESS) {
1745
- throw std::runtime_error("Transcription failed with error code: " + std::to_string(status));
1746
- }
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
+ }
1747
1776
 
1748
- std::string transcribedText;
1749
- if (result.text) {
1750
- transcribedText = std::string(result.text);
1751
- }
1777
+ std::string transcribedText;
1778
+ if (result.text) {
1779
+ transcribedText = std::string(result.text);
1780
+ }
1781
+ float confidence = result.confidence;
1752
1782
 
1753
- // Free the result
1754
- rac_stt_result_free(&result);
1783
+ rac_stt_result_free(&result);
1755
1784
 
1756
- LOGI("Transcription result: %s", transcribedText.c_str());
1757
- 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
+ }
1758
1794
  });
1759
1795
  }
1760
1796
 
@@ -1762,137 +1798,133 @@ std::shared_ptr<Promise<std::string>> HybridRunAnywhereCore::transcribeFile(
1762
1798
  const std::string& filePath,
1763
1799
  const std::optional<std::string>& language) {
1764
1800
  return Promise<std::string>::async([this, filePath, language]() -> std::string {
1765
- LOGI("Transcribing file: %s", filePath.c_str());
1766
-
1767
- rac_handle_t handle = getGlobalSTTHandle();
1768
- if (!handle) {
1769
- throw std::runtime_error("STT component not available. Is an STT backend registered?");
1770
- }
1801
+ try {
1802
+ LOGI("Transcribing file: %s", filePath.c_str());
1771
1803
 
1772
- if (rac_stt_component_is_loaded(handle) != RAC_TRUE) {
1773
- throw std::runtime_error("No STT model loaded. Call loadSTTModel first.");
1774
- }
1804
+ rac_handle_t handle = getGlobalSTTHandle();
1805
+ if (!handle) {
1806
+ return "{\"error\":\"STT component not available. Is an STT backend registered?\"}";
1807
+ }
1775
1808
 
1776
- // Open the file
1777
- FILE* file = fopen(filePath.c_str(), "rb");
1778
- if (!file) {
1779
- throw std::runtime_error("Failed to open audio file: " + filePath);
1780
- }
1809
+ if (rac_stt_component_is_loaded(handle) != RAC_TRUE) {
1810
+ return "{\"error\":\"No STT model loaded. Call loadSTTModel first.\"}";
1811
+ }
1781
1812
 
1782
- // Get file size
1783
- fseek(file, 0, SEEK_END);
1784
- long fileSize = ftell(file);
1785
- 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
+ }
1786
1818
 
1787
- if (fileSize <= 0) {
1788
- fclose(file);
1789
- throw std::runtime_error("Audio file is empty: " + filePath);
1790
- }
1819
+ // Get file size
1820
+ fseek(file, 0, SEEK_END);
1821
+ long fileSize = ftell(file);
1822
+ fseek(file, 0, SEEK_SET);
1791
1823
 
1792
- LOGI("File size: %ld bytes", fileSize);
1824
+ if (fileSize <= 0) {
1825
+ fclose(file);
1826
+ return "{\"error\":\"Audio file is empty\"}";
1827
+ }
1793
1828
 
1794
- // Read the entire file into memory
1795
- std::vector<uint8_t> fileData(fileSize);
1796
- size_t bytesRead = fread(fileData.data(), 1, fileSize, file);
1797
- fclose(file);
1829
+ LOGI("File size: %ld bytes", fileSize);
1798
1830
 
1799
- if (bytesRead != static_cast<size_t>(fileSize)) {
1800
- throw std::runtime_error("Failed to read audio file completely");
1801
- }
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);
1802
1835
 
1803
- // Parse WAV header to extract audio data
1804
- // WAV header: RIFF chunk (12 bytes) + fmt chunk + data chunk
1805
- // 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
+ }
1806
1839
 
1807
- const uint8_t* data = fileData.data();
1808
- size_t dataSize = fileData.size();
1809
- 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;
1810
1844
 
1811
- // Check RIFF header
1812
- if (dataSize < 44) {
1813
- throw std::runtime_error("File too small to be a valid WAV file");
1814
- }
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
+ }
1815
1854
 
1816
- // Check "RIFF" signature
1817
- if (data[0] != 'R' || data[1] != 'I' || data[2] != 'F' || data[3] != 'F') {
1818
- throw std::runtime_error("Invalid WAV file: missing RIFF header");
1819
- }
1855
+ size_t pos = 12;
1856
+ size_t audioDataOffset = 0;
1857
+ size_t audioDataSize = 0;
1820
1858
 
1821
- // Check "WAVE" format
1822
- if (data[8] != 'W' || data[9] != 'A' || data[10] != 'V' || data[11] != 'E') {
1823
- throw std::runtime_error("Invalid WAV file: missing WAVE format");
1824
- }
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]);
1825
1863
 
1826
- // Find "fmt " and "data" chunks
1827
- size_t pos = 12;
1828
- size_t audioDataOffset = 0;
1829
- 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
+ }
1830
1876
 
1831
- while (pos + 8 < dataSize) {
1832
- char chunkId[5] = {0};
1833
- memcpy(chunkId, &data[pos], 4);
1834
- uint32_t chunkSize = *reinterpret_cast<const uint32_t*>(&data[pos + 4]);
1877
+ pos += 8 + chunkSize;
1878
+ if (chunkSize % 2 != 0) pos++;
1879
+ }
1835
1880
 
1836
- if (strcmp(chunkId, "fmt ") == 0) {
1837
- // Parse fmt chunk
1838
- if (pos + 8 + chunkSize <= dataSize && chunkSize >= 16) {
1839
- // Bytes 12-13: Audio format (1 = PCM)
1840
- // Bytes 14-15: Number of channels
1841
- // Bytes 16-19: Sample rate
1842
- sampleRate = *reinterpret_cast<const int32_t*>(&data[pos + 12]);
1843
- LOGI("WAV sample rate: %d Hz", sampleRate);
1844
- }
1845
- } else if (strcmp(chunkId, "data") == 0) {
1846
- // Found data chunk
1847
- audioDataOffset = pos + 8;
1848
- audioDataSize = chunkSize;
1849
- LOGI("Found audio data: offset=%zu, size=%zu", audioDataOffset, audioDataSize);
1850
- break;
1881
+ if (audioDataSize == 0 || audioDataOffset + audioDataSize > dataSize) {
1882
+ return "{\"error\":\"Could not find valid audio data in WAV file\"}";
1851
1883
  }
1852
1884
 
1853
- pos += 8 + chunkSize;
1854
- // Align to 2-byte boundary
1855
- if (chunkSize % 2 != 0) pos++;
1856
- }
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
+ }
1857
1889
 
1858
- if (audioDataSize == 0 || audioDataOffset + audioDataSize > dataSize) {
1859
- throw std::runtime_error("Could not find valid audio data in WAV file");
1860
- }
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
+ }
1861
1896
 
1862
- // Set up transcription options
1863
- rac_stt_options_t options = RAC_STT_OPTIONS_DEFAULT;
1864
- options.sample_rate = sampleRate;
1865
- options.audio_format = RAC_AUDIO_FORMAT_WAV; // Tell the backend it's WAV format
1866
- if (language.has_value() && !language->empty()) {
1867
- options.language = language->c_str();
1868
- }
1897
+ LOGI("Transcribing %zu bytes of audio at %d Hz", audioDataSize, sampleRate);
1869
1898
 
1870
- 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
+ );
1871
1907
 
1872
- // Transcribe - pass the raw PCM data (after WAV header)
1873
- rac_stt_result_t result = {};
1874
- rac_result_t status = rac_stt_component_transcribe(
1875
- handle,
1876
- &data[audioDataOffset],
1877
- audioDataSize,
1878
- &options,
1879
- &result
1880
- );
1908
+ if (status != RAC_SUCCESS) {
1909
+ rac_stt_result_free(&result);
1910
+ return "{\"error\":\"Transcription failed with error code: " + std::to_string(status) + "\"}";
1911
+ }
1881
1912
 
1882
- if (status != RAC_SUCCESS) {
1883
- throw std::runtime_error("Transcription failed with error code: " + std::to_string(status));
1884
- }
1913
+ std::string transcribedText;
1914
+ if (result.text) {
1915
+ transcribedText = std::string(result.text);
1916
+ }
1885
1917
 
1886
- std::string transcribedText;
1887
- if (result.text) {
1888
- 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)\"}";
1889
1927
  }
1890
-
1891
- // Free the result
1892
- rac_stt_result_free(&result);
1893
-
1894
- LOGI("Transcription result: %s", transcribedText.c_str());
1895
- return transcribedText;
1896
1928
  });
1897
1929
  }
1898
1930
 
@@ -2569,4 +2601,71 @@ std::shared_ptr<Promise<bool>> HybridRunAnywhereCore::isTelemetryInitialized() {
2569
2601
  });
2570
2602
  }
2571
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
+
2572
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_;