cactus-react-native 1.2.1 → 1.4.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 (214) hide show
  1. package/README.md +555 -8
  2. package/android/CMakeLists.txt +4 -3
  3. package/android/src/main/java/com/margelo/nitro/cactus/HybridCactusFileSystem.kt +20 -1
  4. package/android/src/main/jniLibs/arm64-v8a/libcactus.a +0 -0
  5. package/android/src/main/jniLibs/arm64-v8a/libcactus_util.a +0 -0
  6. package/cpp/HybridCactus.cpp +112 -19
  7. package/cpp/HybridCactus.hpp +12 -3
  8. package/cpp/HybridCactusIndex.cpp +325 -0
  9. package/cpp/HybridCactusIndex.hpp +43 -0
  10. package/cpp/HybridCactusUtil.cpp +3 -3
  11. package/cpp/HybridCactusUtil.hpp +2 -1
  12. package/cpp/cactus_ffi.h +83 -2
  13. package/cpp/cactus_util.h +1 -1
  14. package/ios/HybridCactusFileSystem.swift +23 -2
  15. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus.h +2 -0
  16. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus_ffi.h +83 -2
  17. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus_telemetry.h +656 -0
  18. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/{ffi_utils.h → cactus_utils.h} +104 -17
  19. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/engine.h +117 -7
  20. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/graph.h +91 -5
  21. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/kernel.h +15 -6
  22. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Info.plist +0 -0
  23. package/ios/cactus.xcframework/ios-arm64/cactus.framework/cactus +0 -0
  24. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus.h +2 -0
  25. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus_ffi.h +83 -2
  26. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus_telemetry.h +656 -0
  27. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/{ffi_utils.h → cactus_utils.h} +104 -17
  28. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/engine.h +117 -7
  29. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/graph.h +91 -5
  30. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/kernel.h +15 -6
  31. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Info.plist +0 -0
  32. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/_CodeSignature/CodeResources +1 -1
  33. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/cactus +0 -0
  34. package/ios/cactus_util.xcframework/Info.plist +4 -4
  35. package/ios/cactus_util.xcframework/ios-arm64/cactus_util.framework/Headers/cactus_util.h +1 -1
  36. package/ios/cactus_util.xcframework/ios-arm64/cactus_util.framework/Headers/database.h +27 -0
  37. package/ios/cactus_util.xcframework/ios-arm64/cactus_util.framework/Info.plist +0 -0
  38. package/ios/cactus_util.xcframework/ios-arm64/cactus_util.framework/cactus_util +0 -0
  39. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/Headers/cactus_util.h +1 -1
  40. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/Headers/database.h +27 -0
  41. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/Info.plist +0 -0
  42. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/_CodeSignature/CodeResources +3 -3
  43. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/cactus_util +0 -0
  44. package/lib/module/api/Database.js +12 -3
  45. package/lib/module/api/Database.js.map +1 -1
  46. package/lib/module/classes/CactusIndex.js +45 -0
  47. package/lib/module/classes/CactusIndex.js.map +1 -0
  48. package/lib/module/classes/CactusLM.js +35 -5
  49. package/lib/module/classes/CactusLM.js.map +1 -1
  50. package/lib/module/classes/CactusSTT.js +17 -5
  51. package/lib/module/classes/CactusSTT.js.map +1 -1
  52. package/lib/module/config/CactusConfig.js +2 -0
  53. package/lib/module/config/CactusConfig.js.map +1 -1
  54. package/lib/module/constants/packageVersion.js +1 -1
  55. package/lib/module/hooks/useCactusIndex.js +175 -0
  56. package/lib/module/hooks/useCactusIndex.js.map +1 -0
  57. package/lib/module/hooks/useCactusLM.js +54 -2
  58. package/lib/module/hooks/useCactusLM.js.map +1 -1
  59. package/lib/module/hooks/useCactusSTT.js +2 -2
  60. package/lib/module/hooks/useCactusSTT.js.map +1 -1
  61. package/lib/module/index.js +2 -0
  62. package/lib/module/index.js.map +1 -1
  63. package/lib/module/native/Cactus.js +24 -13
  64. package/lib/module/native/Cactus.js.map +1 -1
  65. package/lib/module/native/CactusFileSystem.js +3 -0
  66. package/lib/module/native/CactusFileSystem.js.map +1 -1
  67. package/lib/module/native/CactusIndex.js +32 -0
  68. package/lib/module/native/CactusIndex.js.map +1 -0
  69. package/lib/module/native/CactusUtil.js +16 -3
  70. package/lib/module/native/CactusUtil.js.map +1 -1
  71. package/lib/module/native/index.js +1 -0
  72. package/lib/module/native/index.js.map +1 -1
  73. package/lib/module/specs/CactusIndex.nitro.js +4 -0
  74. package/lib/module/specs/CactusIndex.nitro.js.map +1 -0
  75. package/lib/module/telemetry/Telemetry.js +3 -1
  76. package/lib/module/telemetry/Telemetry.js.map +1 -1
  77. package/lib/module/types/CactusIndex.js +2 -0
  78. package/lib/module/types/CactusIndex.js.map +1 -0
  79. package/lib/typescript/src/api/Database.d.ts +4 -1
  80. package/lib/typescript/src/api/Database.d.ts.map +1 -1
  81. package/lib/typescript/src/classes/CactusIndex.d.ts +15 -0
  82. package/lib/typescript/src/classes/CactusIndex.d.ts.map +1 -0
  83. package/lib/typescript/src/classes/CactusLM.d.ts +5 -2
  84. package/lib/typescript/src/classes/CactusLM.d.ts.map +1 -1
  85. package/lib/typescript/src/classes/CactusSTT.d.ts +2 -1
  86. package/lib/typescript/src/classes/CactusSTT.d.ts.map +1 -1
  87. package/lib/typescript/src/config/CactusConfig.d.ts +1 -0
  88. package/lib/typescript/src/config/CactusConfig.d.ts.map +1 -1
  89. package/lib/typescript/src/constants/packageVersion.d.ts +1 -1
  90. package/lib/typescript/src/hooks/useCactusIndex.d.ts +14 -0
  91. package/lib/typescript/src/hooks/useCactusIndex.d.ts.map +1 -0
  92. package/lib/typescript/src/hooks/useCactusLM.d.ts +4 -2
  93. package/lib/typescript/src/hooks/useCactusLM.d.ts.map +1 -1
  94. package/lib/typescript/src/hooks/useCactusSTT.d.ts +1 -1
  95. package/lib/typescript/src/hooks/useCactusSTT.d.ts.map +1 -1
  96. package/lib/typescript/src/index.d.ts +4 -1
  97. package/lib/typescript/src/index.d.ts.map +1 -1
  98. package/lib/typescript/src/native/Cactus.d.ts +4 -2
  99. package/lib/typescript/src/native/Cactus.d.ts.map +1 -1
  100. package/lib/typescript/src/native/CactusFileSystem.d.ts +1 -0
  101. package/lib/typescript/src/native/CactusFileSystem.d.ts.map +1 -1
  102. package/lib/typescript/src/native/CactusIndex.d.ts +12 -0
  103. package/lib/typescript/src/native/CactusIndex.d.ts.map +1 -0
  104. package/lib/typescript/src/native/CactusUtil.d.ts.map +1 -1
  105. package/lib/typescript/src/native/index.d.ts +1 -0
  106. package/lib/typescript/src/native/index.d.ts.map +1 -1
  107. package/lib/typescript/src/specs/Cactus.nitro.d.ts +4 -2
  108. package/lib/typescript/src/specs/Cactus.nitro.d.ts.map +1 -1
  109. package/lib/typescript/src/specs/CactusFileSystem.nitro.d.ts +1 -0
  110. package/lib/typescript/src/specs/CactusFileSystem.nitro.d.ts.map +1 -1
  111. package/lib/typescript/src/specs/CactusIndex.nitro.d.ts +24 -0
  112. package/lib/typescript/src/specs/CactusIndex.nitro.d.ts.map +1 -0
  113. package/lib/typescript/src/specs/CactusUtil.nitro.d.ts +1 -1
  114. package/lib/typescript/src/specs/CactusUtil.nitro.d.ts.map +1 -1
  115. package/lib/typescript/src/types/CactusIndex.d.ts +34 -0
  116. package/lib/typescript/src/types/CactusIndex.d.ts.map +1 -0
  117. package/lib/typescript/src/types/CactusLM.d.ts +17 -0
  118. package/lib/typescript/src/types/CactusLM.d.ts.map +1 -1
  119. package/lib/typescript/src/types/CactusSTT.d.ts +1 -1
  120. package/lib/typescript/src/types/CactusSTT.d.ts.map +1 -1
  121. package/nitro.json +3 -0
  122. package/nitrogen/generated/android/c++/JDeviceInfo.hpp +1 -1
  123. package/nitrogen/generated/android/c++/JFunc_void_double.hpp +1 -1
  124. package/nitrogen/generated/android/c++/JHybridCactusCryptoSpec.cpp +1 -1
  125. package/nitrogen/generated/android/c++/JHybridCactusCryptoSpec.hpp +1 -1
  126. package/nitrogen/generated/android/c++/JHybridCactusDeviceInfoSpec.cpp +1 -1
  127. package/nitrogen/generated/android/c++/JHybridCactusDeviceInfoSpec.hpp +1 -1
  128. package/nitrogen/generated/android/c++/JHybridCactusFileSystemSpec.cpp +17 -1
  129. package/nitrogen/generated/android/c++/JHybridCactusFileSystemSpec.hpp +2 -1
  130. package/nitrogen/generated/android/c++/JHybridCactusImageSpec.cpp +1 -1
  131. package/nitrogen/generated/android/c++/JHybridCactusImageSpec.hpp +1 -1
  132. package/nitrogen/generated/android/cactus+autolinking.cmake +2 -1
  133. package/nitrogen/generated/android/cactus+autolinking.gradle +1 -1
  134. package/nitrogen/generated/android/cactusOnLoad.cpp +11 -1
  135. package/nitrogen/generated/android/cactusOnLoad.hpp +1 -1
  136. package/nitrogen/generated/android/kotlin/com/margelo/nitro/cactus/DeviceInfo.kt +1 -1
  137. package/nitrogen/generated/android/kotlin/com/margelo/nitro/cactus/Func_void_double.kt +1 -1
  138. package/nitrogen/generated/android/kotlin/com/margelo/nitro/cactus/HybridCactusCryptoSpec.kt +1 -1
  139. package/nitrogen/generated/android/kotlin/com/margelo/nitro/cactus/HybridCactusDeviceInfoSpec.kt +1 -1
  140. package/nitrogen/generated/android/kotlin/com/margelo/nitro/cactus/HybridCactusFileSystemSpec.kt +5 -1
  141. package/nitrogen/generated/android/kotlin/com/margelo/nitro/cactus/HybridCactusImageSpec.kt +1 -1
  142. package/nitrogen/generated/android/kotlin/com/margelo/nitro/cactus/cactusOnLoad.kt +1 -1
  143. package/nitrogen/generated/ios/Cactus+autolinking.rb +1 -1
  144. package/nitrogen/generated/ios/Cactus-Swift-Cxx-Bridge.cpp +1 -1
  145. package/nitrogen/generated/ios/Cactus-Swift-Cxx-Bridge.hpp +1 -1
  146. package/nitrogen/generated/ios/Cactus-Swift-Cxx-Umbrella.hpp +1 -1
  147. package/nitrogen/generated/ios/CactusAutolinking.mm +11 -1
  148. package/nitrogen/generated/ios/CactusAutolinking.swift +1 -1
  149. package/nitrogen/generated/ios/c++/HybridCactusCryptoSpecSwift.cpp +1 -1
  150. package/nitrogen/generated/ios/c++/HybridCactusCryptoSpecSwift.hpp +1 -1
  151. package/nitrogen/generated/ios/c++/HybridCactusDeviceInfoSpecSwift.cpp +1 -1
  152. package/nitrogen/generated/ios/c++/HybridCactusDeviceInfoSpecSwift.hpp +1 -1
  153. package/nitrogen/generated/ios/c++/HybridCactusFileSystemSpecSwift.cpp +1 -1
  154. package/nitrogen/generated/ios/c++/HybridCactusFileSystemSpecSwift.hpp +9 -1
  155. package/nitrogen/generated/ios/c++/HybridCactusImageSpecSwift.cpp +1 -1
  156. package/nitrogen/generated/ios/c++/HybridCactusImageSpecSwift.hpp +1 -1
  157. package/nitrogen/generated/ios/swift/DeviceInfo.swift +1 -1
  158. package/nitrogen/generated/ios/swift/Func_void.swift +1 -1
  159. package/nitrogen/generated/ios/swift/Func_void_DeviceInfo.swift +1 -1
  160. package/nitrogen/generated/ios/swift/Func_void_bool.swift +1 -1
  161. package/nitrogen/generated/ios/swift/Func_void_double.swift +1 -1
  162. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +1 -1
  163. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__string_.swift +1 -1
  164. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +1 -1
  165. package/nitrogen/generated/ios/swift/HybridCactusCryptoSpec.swift +1 -1
  166. package/nitrogen/generated/ios/swift/HybridCactusCryptoSpec_cxx.swift +1 -1
  167. package/nitrogen/generated/ios/swift/HybridCactusDeviceInfoSpec.swift +1 -1
  168. package/nitrogen/generated/ios/swift/HybridCactusDeviceInfoSpec_cxx.swift +1 -1
  169. package/nitrogen/generated/ios/swift/HybridCactusFileSystemSpec.swift +2 -1
  170. package/nitrogen/generated/ios/swift/HybridCactusFileSystemSpec_cxx.swift +20 -1
  171. package/nitrogen/generated/ios/swift/HybridCactusImageSpec.swift +1 -1
  172. package/nitrogen/generated/ios/swift/HybridCactusImageSpec_cxx.swift +1 -1
  173. package/nitrogen/generated/shared/c++/CactusIndexGetResult.hpp +84 -0
  174. package/nitrogen/generated/shared/c++/CactusIndexQueryResult.hpp +79 -0
  175. package/nitrogen/generated/shared/c++/DeviceInfo.hpp +1 -1
  176. package/nitrogen/generated/shared/c++/HybridCactusCryptoSpec.cpp +1 -1
  177. package/nitrogen/generated/shared/c++/HybridCactusCryptoSpec.hpp +1 -1
  178. package/nitrogen/generated/shared/c++/HybridCactusDeviceInfoSpec.cpp +1 -1
  179. package/nitrogen/generated/shared/c++/HybridCactusDeviceInfoSpec.hpp +1 -1
  180. package/nitrogen/generated/shared/c++/HybridCactusFileSystemSpec.cpp +2 -1
  181. package/nitrogen/generated/shared/c++/HybridCactusFileSystemSpec.hpp +2 -1
  182. package/nitrogen/generated/shared/c++/HybridCactusImageSpec.cpp +1 -1
  183. package/nitrogen/generated/shared/c++/HybridCactusImageSpec.hpp +1 -1
  184. package/nitrogen/generated/shared/c++/HybridCactusIndexSpec.cpp +27 -0
  185. package/nitrogen/generated/shared/c++/HybridCactusIndexSpec.hpp +76 -0
  186. package/nitrogen/generated/shared/c++/HybridCactusSpec.cpp +3 -1
  187. package/nitrogen/generated/shared/c++/HybridCactusSpec.hpp +6 -3
  188. package/nitrogen/generated/shared/c++/HybridCactusUtilSpec.cpp +1 -1
  189. package/nitrogen/generated/shared/c++/HybridCactusUtilSpec.hpp +2 -2
  190. package/package.json +2 -2
  191. package/src/api/Database.ts +14 -2
  192. package/src/classes/CactusIndex.ts +58 -0
  193. package/src/classes/CactusLM.ts +41 -5
  194. package/src/classes/CactusSTT.ts +19 -6
  195. package/src/config/CactusConfig.ts +3 -0
  196. package/src/constants/packageVersion.ts +1 -1
  197. package/src/hooks/useCactusIndex.ts +195 -0
  198. package/src/hooks/useCactusLM.ts +63 -3
  199. package/src/hooks/useCactusSTT.ts +2 -2
  200. package/src/index.tsx +16 -0
  201. package/src/native/Cactus.ts +40 -13
  202. package/src/native/CactusFileSystem.ts +4 -0
  203. package/src/native/CactusIndex.ts +54 -0
  204. package/src/native/CactusUtil.ts +19 -3
  205. package/src/native/index.ts +1 -0
  206. package/src/specs/Cactus.nitro.ts +13 -2
  207. package/src/specs/CactusFileSystem.nitro.ts +2 -0
  208. package/src/specs/CactusIndex.nitro.ts +31 -0
  209. package/src/specs/CactusUtil.nitro.ts +1 -1
  210. package/src/telemetry/Telemetry.ts +1 -1
  211. package/src/types/CactusIndex.ts +40 -0
  212. package/src/types/CactusLM.ts +21 -0
  213. package/src/types/CactusSTT.ts +1 -1
  214. package/android/src/main/jniLibs/arm64-v8a/libcactus_util.so +0 -0
@@ -209,6 +209,8 @@ class HybridCactusFileSystem : HybridCactusFileSystemSpec() {
209
209
  modelFile.deleteRecursively()
210
210
  }
211
211
 
212
+ override fun getIndexPath(name: String): Promise<String> = Promise.async { indexFile(name).absolutePath }
213
+
212
214
  private fun cactusFile(): File {
213
215
  val cactusDir = File(context.filesDir, "cactus")
214
216
 
@@ -221,6 +223,23 @@ class HybridCactusFileSystem : HybridCactusFileSystemSpec() {
221
223
 
222
224
  private fun modelFile(model: String): File {
223
225
  val cactusDir = cactusFile()
224
- return File(cactusDir, "models/$model")
226
+ val modelsDir = File(cactusDir, "models")
227
+
228
+ if (!modelsDir.exists()) {
229
+ modelsDir.mkdirs()
230
+ }
231
+
232
+ return File(modelsDir, model)
233
+ }
234
+
235
+ private fun indexFile(name: String): File {
236
+ val cactusDir = cactusFile()
237
+ val finalDir = File(cactusDir, "indexes/$name")
238
+
239
+ if (!finalDir.exists()) {
240
+ finalDir.mkdirs()
241
+ }
242
+
243
+ return finalDir
225
244
  }
226
245
  }
@@ -1,6 +1,7 @@
1
1
  #include "HybridCactus.hpp"
2
2
 
3
3
  namespace margelo::nitro::cactus {
4
+
4
5
  HybridCactus::HybridCactus() : HybridObject(TAG) {}
5
6
 
6
7
  std::shared_ptr<Promise<void>>
@@ -19,7 +20,8 @@ HybridCactus::init(const std::string &modelPath, double contextSize,
19
20
  corpusDir ? corpusDir->c_str() : nullptr);
20
21
 
21
22
  if (!model) {
22
- throw std::runtime_error("Failed to initialize Cactus model");
23
+ throw std::runtime_error("Cactus init failed: " +
24
+ std::string(cactus_get_last_error()));
23
25
  }
24
26
 
25
27
  this->_model = model;
@@ -65,7 +67,8 @@ std::shared_ptr<Promise<std::string>> HybridCactus::complete(
65
67
  cactusTokenCallback, &callbackCtx);
66
68
 
67
69
  if (result < 0) {
68
- throw std::runtime_error("Cactus completion failed");
70
+ throw std::runtime_error("Cactus complete failed: " +
71
+ std::string(cactus_get_last_error()));
69
72
  }
70
73
 
71
74
  // Remove null terminator
@@ -75,12 +78,78 @@ std::shared_ptr<Promise<std::string>> HybridCactus::complete(
75
78
  });
76
79
  }
77
80
 
81
+ std::shared_ptr<Promise<std::vector<double>>>
82
+ HybridCactus::tokenize(const std::string &text) {
83
+ return Promise<std::vector<double>>::async([this,
84
+ text]() -> std::vector<double> {
85
+ std::lock_guard<std::mutex> lock(this->_modelMutex);
86
+
87
+ if (!this->_model) {
88
+ throw std::runtime_error("Cactus model is not initialized");
89
+ }
90
+
91
+ std::vector<uint32_t> tokenBuffer(text.length() * 2 + 16);
92
+ size_t outTokenLen = 0;
93
+
94
+ int result = cactus_tokenize(this->_model, text.c_str(), tokenBuffer.data(),
95
+ tokenBuffer.size(), &outTokenLen);
96
+
97
+ if (result < 0) {
98
+ throw std::runtime_error("Cactus tokenize failed: " +
99
+ std::string(cactus_get_last_error()));
100
+ }
101
+
102
+ tokenBuffer.resize(outTokenLen);
103
+
104
+ return std::vector<double>(tokenBuffer.begin(), tokenBuffer.end());
105
+ });
106
+ }
107
+
108
+ std::shared_ptr<Promise<std::string>>
109
+ HybridCactus::scoreWindow(const std::vector<double> &tokens, double start,
110
+ double end, double context) {
111
+ return Promise<std::string>::async(
112
+ [this, tokens, start, end, context]() -> std::string {
113
+ std::lock_guard<std::mutex> lock(this->_modelMutex);
114
+
115
+ if (!this->_model) {
116
+ throw std::runtime_error("Cactus model is not initialized");
117
+ }
118
+
119
+ std::vector<uint32_t> tokenBuffer;
120
+ tokenBuffer.reserve(tokens.size());
121
+ for (double d : tokens) {
122
+ tokenBuffer.emplace_back(static_cast<uint32_t>(d));
123
+ }
124
+
125
+ std::string responseBuffer;
126
+ responseBuffer.resize(1024);
127
+
128
+ int result = cactus_score_window(
129
+ this->_model, tokenBuffer.data(), tokenBuffer.size(),
130
+ static_cast<size_t>(start), static_cast<size_t>(end),
131
+ static_cast<size_t>(context), responseBuffer.data(),
132
+ responseBuffer.size());
133
+
134
+ if (result < 0) {
135
+ throw std::runtime_error("Cactus score window failed: " +
136
+ std::string(cactus_get_last_error()));
137
+ }
138
+
139
+ // Remove null terminator
140
+ responseBuffer.resize(strlen(responseBuffer.c_str()));
141
+
142
+ return responseBuffer;
143
+ });
144
+ }
145
+
78
146
  std::shared_ptr<Promise<std::string>> HybridCactus::transcribe(
79
- const std::string &audioFilePath, const std::string &prompt,
80
- double responseBufferSize, const std::optional<std::string> &optionsJson,
147
+ const std::variant<std::vector<double>, std::string> &audio,
148
+ const std::string &prompt, double responseBufferSize,
149
+ const std::optional<std::string> &optionsJson,
81
150
  const std::optional<std::function<void(const std::string & /* token */,
82
151
  double /* tokenId */)>> &callback) {
83
- return Promise<std::string>::async([this, audioFilePath, prompt, optionsJson,
152
+ return Promise<std::string>::async([this, audio, prompt, optionsJson,
84
153
  callback,
85
154
  responseBufferSize]() -> std::string {
86
155
  std::lock_guard<std::mutex> lock(this->_modelMutex);
@@ -105,14 +174,34 @@ std::shared_ptr<Promise<std::string>> HybridCactus::transcribe(
105
174
  std::string responseBuffer;
106
175
  responseBuffer.resize(responseBufferSize);
107
176
 
108
- int result =
109
- cactus_transcribe(this->_model, audioFilePath.c_str(), prompt.c_str(),
110
- responseBuffer.data(), responseBufferSize,
111
- optionsJson ? optionsJson->c_str() : nullptr,
112
- cactusTokenCallback, &callbackCtx);
177
+ int result;
178
+ if (std::holds_alternative<std::string>(audio)) {
179
+ result = cactus_transcribe(
180
+ this->_model, std::get<std::string>(audio).c_str(), prompt.c_str(),
181
+ responseBuffer.data(), responseBufferSize,
182
+ optionsJson ? optionsJson->c_str() : nullptr, cactusTokenCallback,
183
+ &callbackCtx, nullptr, 0);
184
+ } else {
185
+ const auto &audioDoubles = std::get<std::vector<double>>(audio);
186
+
187
+ std::vector<uint8_t> audioBytes;
188
+ audioBytes.reserve(audioDoubles.size());
189
+
190
+ for (double d : audioDoubles) {
191
+ d = std::clamp(d, 0.0, 255.0);
192
+ audioBytes.emplace_back(static_cast<uint8_t>(d));
193
+ }
194
+
195
+ result = cactus_transcribe(this->_model, nullptr, prompt.c_str(),
196
+ responseBuffer.data(), responseBufferSize,
197
+ optionsJson ? optionsJson->c_str() : nullptr,
198
+ cactusTokenCallback, &callbackCtx,
199
+ audioBytes.data(), audioBytes.size());
200
+ }
113
201
 
114
202
  if (result < 0) {
115
- throw std::runtime_error("Cactus transcription failed");
203
+ throw std::runtime_error("Cactus transcribe failed: " +
204
+ std::string(cactus_get_last_error()));
116
205
  }
117
206
 
118
207
  // Remove null terminator
@@ -123,9 +212,10 @@ std::shared_ptr<Promise<std::string>> HybridCactus::transcribe(
123
212
  }
124
213
 
125
214
  std::shared_ptr<Promise<std::vector<double>>>
126
- HybridCactus::embed(const std::string &text, double embeddingBufferSize) {
215
+ HybridCactus::embed(const std::string &text, double embeddingBufferSize,
216
+ bool normalize) {
127
217
  return Promise<std::vector<double>>::async(
128
- [this, text, embeddingBufferSize]() -> std::vector<double> {
218
+ [this, text, embeddingBufferSize, normalize]() -> std::vector<double> {
129
219
  std::lock_guard<std::mutex> lock(this->_modelMutex);
130
220
 
131
221
  if (!this->_model) {
@@ -135,12 +225,13 @@ HybridCactus::embed(const std::string &text, double embeddingBufferSize) {
135
225
  std::vector<float> embeddingBuffer(embeddingBufferSize);
136
226
  size_t embeddingDim;
137
227
 
138
- int result =
139
- cactus_embed(this->_model, text.c_str(), embeddingBuffer.data(),
140
- embeddingBufferSize * sizeof(float), &embeddingDim);
228
+ int result = cactus_embed(
229
+ this->_model, text.c_str(), embeddingBuffer.data(),
230
+ embeddingBufferSize * sizeof(float), &embeddingDim, normalize);
141
231
 
142
232
  if (result < 0) {
143
- throw std::runtime_error("Cactus embedding failed");
233
+ throw std::runtime_error("Cactus embed failed: " +
234
+ std::string(cactus_get_last_error()));
144
235
  }
145
236
 
146
237
  embeddingBuffer.resize(embeddingDim);
@@ -169,7 +260,8 @@ HybridCactus::imageEmbed(const std::string &imagePath,
169
260
  embeddingBufferSize * sizeof(float), &embeddingDim);
170
261
 
171
262
  if (result < 0) {
172
- throw std::runtime_error("Cactus image embedding failed");
263
+ throw std::runtime_error("Cactus image embed failed: " +
264
+ std::string(cactus_get_last_error()));
173
265
  }
174
266
 
175
267
  embeddingBuffer.resize(embeddingDim);
@@ -198,7 +290,8 @@ HybridCactus::audioEmbed(const std::string &audioPath,
198
290
  embeddingBufferSize * sizeof(float), &embeddingDim);
199
291
 
200
292
  if (result < 0) {
201
- throw std::runtime_error("Cactus audio embedding failed");
293
+ throw std::runtime_error("Cactus audio embed failed: " +
294
+ std::string(cactus_get_last_error()));
202
295
  }
203
296
 
204
297
  embeddingBuffer.resize(embeddingDim);
@@ -23,15 +23,24 @@ public:
23
23
  double /* tokenId */)>> &callback)
24
24
  override;
25
25
 
26
+ std::shared_ptr<Promise<std::vector<double>>>
27
+ tokenize(const std::string &text) override;
28
+
29
+ std::shared_ptr<Promise<std::string>>
30
+ scoreWindow(const std::vector<double> &tokens, double start, double end,
31
+ double context) override;
32
+
26
33
  std::shared_ptr<Promise<std::string>> transcribe(
27
- const std::string &audioFilePath, const std::string &prompt,
28
- double responseBufferSize, const std::optional<std::string> &optionsJson,
34
+ const std::variant<std::vector<double>, std::string> &audio,
35
+ const std::string &prompt, double responseBufferSize,
36
+ const std::optional<std::string> &optionsJson,
29
37
  const std::optional<std::function<void(const std::string & /* token */,
30
38
  double /* tokenId */)>> &callback)
31
39
  override;
32
40
 
33
41
  std::shared_ptr<Promise<std::vector<double>>>
34
- embed(const std::string &text, double embeddingBufferSize) override;
42
+ embed(const std::string &text, double embeddingBufferSize,
43
+ bool normalize) override;
35
44
 
36
45
  std::shared_ptr<Promise<std::vector<double>>>
37
46
  imageEmbed(const std::string &imagePath, double embeddingBufferSize) override;
@@ -0,0 +1,325 @@
1
+ #include "HybridCactusIndex.hpp"
2
+
3
+ namespace margelo::nitro::cactus {
4
+
5
+ HybridCactusIndex::HybridCactusIndex() : HybridObject(TAG) {}
6
+
7
+ std::shared_ptr<Promise<void>>
8
+ HybridCactusIndex::init(const std::string &indexPath, double embeddingDim) {
9
+ return Promise<void>::async([this, indexPath, embeddingDim]() -> void {
10
+ std::lock_guard<std::mutex> lock(this->_indexMutex);
11
+
12
+ if (this->_index) {
13
+ throw std::runtime_error("Cactus index is already initialized");
14
+ }
15
+
16
+ const cactus_index_t index =
17
+ cactus_index_init(indexPath.c_str(), embeddingDim);
18
+
19
+ if (!index) {
20
+ throw std::runtime_error("Cactus index init failed: " +
21
+ std::string(cactus_get_last_error()));
22
+ }
23
+
24
+ this->_index = index;
25
+ this->_embeddingDim = static_cast<size_t>(embeddingDim);
26
+ });
27
+ }
28
+
29
+ std::shared_ptr<Promise<void>> HybridCactusIndex::add(
30
+ const std::vector<double> &ids, const std::vector<std::string> &documents,
31
+ const std::vector<std::vector<double>> &embeddings,
32
+ const std::optional<std::vector<std::string>> &metadatas) {
33
+ return Promise<void>::async([this, ids, documents, embeddings,
34
+ metadatas]() -> void {
35
+ std::lock_guard<std::mutex> lock(this->_indexMutex);
36
+
37
+ if (!this->_index) {
38
+ throw std::runtime_error("Cactus index is not initialized");
39
+ }
40
+
41
+ const size_t count = ids.size();
42
+ if (documents.size() != count || embeddings.size() != count) {
43
+ throw std::runtime_error(
44
+ "ids, documents, and embeddings must have the same length");
45
+ }
46
+
47
+ if (metadatas.has_value() && metadatas->size() != count) {
48
+ throw std::runtime_error(
49
+ "metadatas must have the same length as other vectors");
50
+ }
51
+
52
+ std::vector<int> intIds;
53
+ intIds.reserve(count);
54
+ for (size_t i = 0; i < count; ++i) {
55
+ intIds.emplace_back(static_cast<int>(ids[i]));
56
+ }
57
+
58
+ std::vector<const char *> documentPtrs;
59
+ documentPtrs.reserve(count);
60
+ for (size_t i = 0; i < count; ++i) {
61
+ documentPtrs.emplace_back(documents[i].c_str());
62
+ }
63
+
64
+ std::vector<std::vector<float>> embeddingsFloat(count);
65
+ std::vector<const float *> embeddingPtrs;
66
+ embeddingPtrs.reserve(count);
67
+ for (size_t i = 0; i < count; ++i) {
68
+ embeddingsFloat[i].resize(embeddings[i].size());
69
+ for (size_t j = 0; j < embeddings[i].size(); ++j) {
70
+ embeddingsFloat[i][j] = static_cast<float>(embeddings[i][j]);
71
+ }
72
+ embeddingPtrs.emplace_back(embeddingsFloat[i].data());
73
+ }
74
+
75
+ int result;
76
+ if (metadatas.has_value()) {
77
+ std::vector<const char *> metadataPtrs;
78
+ metadataPtrs.reserve(count);
79
+ for (size_t i = 0; i < count; ++i) {
80
+ metadataPtrs.emplace_back((*metadatas)[i].c_str());
81
+ }
82
+ result = cactus_index_add(
83
+ this->_index, intIds.data(), documentPtrs.data(), metadataPtrs.data(),
84
+ embeddingPtrs.data(), count, this->_embeddingDim);
85
+ } else {
86
+ result = cactus_index_add(
87
+ this->_index, intIds.data(), documentPtrs.data(), nullptr,
88
+ embeddingPtrs.data(), count, this->_embeddingDim);
89
+ }
90
+
91
+ if (result < 0) {
92
+ throw std::runtime_error("Cactus index add failed: " +
93
+ std::string(cactus_get_last_error()));
94
+ }
95
+ });
96
+ }
97
+
98
+ std::shared_ptr<Promise<void>>
99
+ HybridCactusIndex::_delete(const std::vector<double> &ids) {
100
+ return Promise<void>::async([this, ids]() -> void {
101
+ std::lock_guard<std::mutex> lock(this->_indexMutex);
102
+
103
+ if (!this->_index) {
104
+ throw std::runtime_error("Cactus index is not initialized");
105
+ }
106
+
107
+ std::vector<int> intIds;
108
+ intIds.reserve(ids.size());
109
+ for (size_t i = 0; i < ids.size(); ++i) {
110
+ intIds.emplace_back(static_cast<int>(ids[i]));
111
+ }
112
+
113
+ int result = cactus_index_delete(this->_index, intIds.data(), ids.size());
114
+
115
+ if (result < 0) {
116
+ throw std::runtime_error("Cactus index delete failed: " +
117
+ std::string(cactus_get_last_error()));
118
+ }
119
+ });
120
+ }
121
+
122
+ std::shared_ptr<Promise<CactusIndexGetResult>>
123
+ HybridCactusIndex::get(const std::vector<double> &ids) {
124
+ return Promise<CactusIndexGetResult>::async([this,
125
+ ids]() -> CactusIndexGetResult {
126
+ std::lock_guard<std::mutex> lock(this->_indexMutex);
127
+
128
+ if (!this->_index) {
129
+ throw std::runtime_error("Cactus index is not initialized");
130
+ }
131
+
132
+ const size_t count = ids.size();
133
+
134
+ std::vector<int> intIds;
135
+ intIds.reserve(count);
136
+ for (size_t i = 0; i < count; ++i) {
137
+ intIds.emplace_back(static_cast<int>(ids[i]));
138
+ }
139
+
140
+ std::vector<std::unique_ptr<char[]>> documentBuffers;
141
+ documentBuffers.reserve(count);
142
+ std::vector<std::unique_ptr<char[]>> metadataBuffers;
143
+ metadataBuffers.reserve(count);
144
+ std::vector<std::unique_ptr<float[]>> embeddingBuffers;
145
+ embeddingBuffers.reserve(count);
146
+
147
+ const size_t maxStringSize = 65535;
148
+ std::vector<size_t> documentBufferSizes(count, maxStringSize);
149
+ std::vector<size_t> metadataBufferSizes(count, maxStringSize);
150
+ std::vector<size_t> embeddingBufferSizes(count, this->_embeddingDim);
151
+
152
+ std::vector<char *> documentPtrs;
153
+ documentPtrs.reserve(count);
154
+ std::vector<char *> metadataPtrs;
155
+ metadataPtrs.reserve(count);
156
+ std::vector<float *> embeddingPtrs;
157
+ embeddingPtrs.reserve(count);
158
+
159
+ for (size_t i = 0; i < count; ++i) {
160
+ documentBuffers.emplace_back(std::make_unique<char[]>(maxStringSize));
161
+ documentPtrs.emplace_back(documentBuffers[i].get());
162
+
163
+ metadataBuffers.emplace_back(std::make_unique<char[]>(maxStringSize));
164
+ metadataPtrs.emplace_back(metadataBuffers[i].get());
165
+
166
+ embeddingBuffers.emplace_back(
167
+ std::make_unique<float[]>(this->_embeddingDim));
168
+ embeddingPtrs.emplace_back(embeddingBuffers[i].get());
169
+ }
170
+
171
+ int result =
172
+ cactus_index_get(this->_index, intIds.data(), count,
173
+ documentPtrs.data(), documentBufferSizes.data(),
174
+ metadataPtrs.data(), metadataBufferSizes.data(),
175
+ embeddingPtrs.data(), embeddingBufferSizes.data());
176
+
177
+ if (result < 0) {
178
+ throw std::runtime_error("Cactus index get failed: " +
179
+ std::string(cactus_get_last_error()));
180
+ }
181
+
182
+ CactusIndexGetResult resultObj;
183
+ resultObj.documents.reserve(count);
184
+ resultObj.metadatas.reserve(count);
185
+ resultObj.embeddings = std::vector<std::vector<double>>(count);
186
+
187
+ for (size_t i = 0; i < count; ++i) {
188
+ resultObj.documents.emplace_back(std::string(documentBuffers[i].get()));
189
+ resultObj.metadatas.emplace_back(std::string(metadataBuffers[i].get()));
190
+
191
+ resultObj.embeddings[i].reserve(this->_embeddingDim);
192
+ for (size_t j = 0; j < this->_embeddingDim; ++j) {
193
+ resultObj.embeddings[i].emplace_back(
194
+ static_cast<double>(embeddingBuffers[i].get()[j]));
195
+ }
196
+ }
197
+
198
+ return resultObj;
199
+ });
200
+ }
201
+
202
+ std::shared_ptr<Promise<CactusIndexQueryResult>>
203
+ HybridCactusIndex::query(const std::vector<std::vector<double>> &embeddings,
204
+ const std::optional<std::string> &optionsJson) {
205
+ return Promise<CactusIndexQueryResult>::async(
206
+ [this, embeddings, optionsJson]() -> CactusIndexQueryResult {
207
+ std::lock_guard<std::mutex> lock(this->_indexMutex);
208
+
209
+ if (!this->_index) {
210
+ throw std::runtime_error("Cactus index is not initialized");
211
+ }
212
+
213
+ const size_t count = embeddings.size();
214
+
215
+ std::vector<std::vector<float>> embeddingsFloat(count);
216
+ std::vector<const float *> embeddingPtrs;
217
+ embeddingPtrs.reserve(count);
218
+ for (size_t i = 0; i < count; ++i) {
219
+ embeddingsFloat[i].resize(embeddings[i].size());
220
+ for (size_t j = 0; j < embeddings[i].size(); ++j) {
221
+ embeddingsFloat[i][j] = static_cast<float>(embeddings[i][j]);
222
+ }
223
+ embeddingPtrs.emplace_back(embeddingsFloat[i].data());
224
+ }
225
+
226
+ size_t maxResults = 10;
227
+ if (optionsJson.has_value()) {
228
+ const std::string &json = *optionsJson;
229
+ size_t pos = json.find("\"top_k\"");
230
+ if (pos != std::string::npos) {
231
+ size_t colonPos = json.find(':', pos);
232
+ if (colonPos != std::string::npos) {
233
+ size_t numStart = json.find_first_of("0123456789", colonPos);
234
+ if (numStart != std::string::npos) {
235
+ size_t numEnd = json.find_first_not_of("0123456789", numStart);
236
+ std::string numStr = json.substr(numStart, numEnd - numStart);
237
+ maxResults = std::stoul(numStr);
238
+ }
239
+ }
240
+ }
241
+ }
242
+
243
+ std::vector<size_t> idBufferSizes(count, maxResults);
244
+ std::vector<size_t> scoreBufferSizes(count, maxResults);
245
+
246
+ std::vector<std::unique_ptr<int[]>> idBuffers;
247
+ idBuffers.reserve(count);
248
+ std::vector<std::unique_ptr<float[]>> scoreBuffers;
249
+ scoreBuffers.reserve(count);
250
+
251
+ std::vector<int *> idPtrs;
252
+ idPtrs.reserve(count);
253
+ std::vector<float *> scorePtrs;
254
+ scorePtrs.reserve(count);
255
+
256
+ for (size_t i = 0; i < count; ++i) {
257
+ idBuffers.emplace_back(std::make_unique<int[]>(maxResults));
258
+ idPtrs.emplace_back(idBuffers[i].get());
259
+
260
+ scoreBuffers.emplace_back(std::make_unique<float[]>(maxResults));
261
+ scorePtrs.emplace_back(scoreBuffers[i].get());
262
+ }
263
+
264
+ int result = cactus_index_query(
265
+ this->_index, embeddingPtrs.data(), count, this->_embeddingDim,
266
+ optionsJson ? optionsJson->c_str() : nullptr, idPtrs.data(),
267
+ idBufferSizes.data(), scorePtrs.data(), scoreBufferSizes.data());
268
+
269
+ if (result < 0) {
270
+ throw std::runtime_error("Cactus index query failed: " +
271
+ std::string(cactus_get_last_error()));
272
+ }
273
+
274
+ CactusIndexQueryResult resultObj;
275
+ resultObj.ids = std::vector<std::vector<double>>(count);
276
+ resultObj.scores = std::vector<std::vector<double>>(count);
277
+
278
+ for (size_t i = 0; i < count; ++i) {
279
+ const size_t resultCount = idBufferSizes[i];
280
+ resultObj.ids[i].reserve(resultCount);
281
+ resultObj.scores[i].reserve(resultCount);
282
+
283
+ for (size_t j = 0; j < resultCount; ++j) {
284
+ resultObj.ids[i].emplace_back(
285
+ static_cast<double>(idBuffers[i].get()[j]));
286
+ resultObj.scores[i].emplace_back(
287
+ static_cast<double>(scoreBuffers[i].get()[j]));
288
+ }
289
+ }
290
+
291
+ return resultObj;
292
+ });
293
+ }
294
+
295
+ std::shared_ptr<Promise<void>> HybridCactusIndex::compact() {
296
+ return Promise<void>::async([this]() -> void {
297
+ std::lock_guard<std::mutex> lock(this->_indexMutex);
298
+
299
+ if (!this->_index) {
300
+ throw std::runtime_error("Cactus index is not initialized");
301
+ }
302
+
303
+ int result = cactus_index_compact(this->_index);
304
+
305
+ if (result < 0) {
306
+ throw std::runtime_error("Cactus index compact failed: " +
307
+ std::string(cactus_get_last_error()));
308
+ }
309
+ });
310
+ }
311
+
312
+ std::shared_ptr<Promise<void>> HybridCactusIndex::destroy() {
313
+ return Promise<void>::async([this]() -> void {
314
+ std::lock_guard<std::mutex> lock(this->_indexMutex);
315
+
316
+ if (!this->_index) {
317
+ throw std::runtime_error("Cactus index is not initialized");
318
+ }
319
+
320
+ cactus_index_destroy(this->_index);
321
+ this->_index = nullptr;
322
+ });
323
+ }
324
+
325
+ } // namespace margelo::nitro::cactus
@@ -0,0 +1,43 @@
1
+ #pragma once
2
+ #include "HybridCactusIndexSpec.hpp"
3
+
4
+ #include "cactus_ffi.h"
5
+
6
+ #include <mutex>
7
+
8
+ namespace margelo::nitro::cactus {
9
+
10
+ class HybridCactusIndex : public HybridCactusIndexSpec {
11
+ public:
12
+ HybridCactusIndex();
13
+
14
+ std::shared_ptr<Promise<void>> init(const std::string &indexPath,
15
+ double embeddingDim) override;
16
+
17
+ std::shared_ptr<Promise<void>>
18
+ add(const std::vector<double> &ids, const std::vector<std::string> &documents,
19
+ const std::vector<std::vector<double>> &embeddings,
20
+ const std::optional<std::vector<std::string>> &metadatas) override;
21
+
22
+ std::shared_ptr<Promise<void>>
23
+ _delete(const std::vector<double> &ids) override;
24
+
25
+ std::shared_ptr<Promise<CactusIndexGetResult>>
26
+ get(const std::vector<double> &ids) override;
27
+
28
+ std::shared_ptr<Promise<CactusIndexQueryResult>>
29
+ query(const std::vector<std::vector<double>> &embeddings,
30
+ const std::optional<std::string> &optionsJson) override;
31
+
32
+ std::shared_ptr<Promise<void>> compact() override;
33
+
34
+ std::shared_ptr<Promise<void>> destroy() override;
35
+
36
+ private:
37
+ cactus_index_t _index = nullptr;
38
+ size_t _embeddingDim;
39
+
40
+ std::mutex _indexMutex;
41
+ };
42
+
43
+ } // namespace margelo::nitro::cactus
@@ -23,12 +23,12 @@ HybridCactusUtil::registerApp(const std::string &encryptedData) {
23
23
  }
24
24
 
25
25
  std::shared_ptr<Promise<std::optional<std::string>>>
26
- HybridCactusUtil::getDeviceId() {
26
+ HybridCactusUtil::getDeviceId(const std::optional<std::string> &token) {
27
27
  return Promise<std::optional<std::string>>::async(
28
- [this]() -> std::optional<std::string> {
28
+ [this, token]() -> std::optional<std::string> {
29
29
  std::lock_guard<std::mutex> lock(this->_mutex);
30
30
 
31
- const char *deviceId = get_device_id();
31
+ const char *deviceId = get_device_id(token ? token->c_str() : nullptr);
32
32
  return deviceId ? std::optional<std::string>(deviceId) : std::nullopt;
33
33
  });
34
34
  }
@@ -14,7 +14,8 @@ public:
14
14
  std::shared_ptr<Promise<std::string>>
15
15
  registerApp(const std::string &encryptedData) override;
16
16
 
17
- std::shared_ptr<Promise<std::optional<std::string>>> getDeviceId() override;
17
+ std::shared_ptr<Promise<std::optional<std::string>>>
18
+ getDeviceId(const std::optional<std::string> &token) override;
18
19
 
19
20
  std::shared_ptr<Promise<void>>
20
21
  setAndroidDataDirectory(const std::string &dataDir) override;