cactus-react-native 1.5.0 → 1.7.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 (216) hide show
  1. package/Cactus.podspec +1 -1
  2. package/README.md +347 -241
  3. package/android/CMakeLists.txt +24 -5
  4. package/android/src/main/jniLibs/arm64-v8a/libcactus.a +0 -0
  5. package/android/src/main/jniLibs/arm64-v8a/libcurl.a +0 -0
  6. package/android/src/main/jniLibs/arm64-v8a/libmbedcrypto.a +0 -0
  7. package/android/src/main/jniLibs/arm64-v8a/libmbedtls.a +0 -0
  8. package/android/src/main/jniLibs/arm64-v8a/libmbedx509.a +0 -0
  9. package/cpp/HybridCactus.cpp +149 -117
  10. package/cpp/HybridCactus.hpp +14 -10
  11. package/cpp/cactus_ffi.h +54 -43
  12. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus.h +0 -1
  13. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus_ffi.h +54 -43
  14. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus_utils.h +318 -123
  15. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/engine.h +118 -15
  16. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/graph.h +77 -32
  17. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/kernel.h +68 -6
  18. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/kernel_utils.h +21 -155
  19. package/ios/cactus.xcframework/ios-arm64/cactus.framework/cactus +0 -0
  20. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus.h +0 -1
  21. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus_ffi.h +54 -43
  22. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus_utils.h +318 -123
  23. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/engine.h +118 -15
  24. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/graph.h +77 -32
  25. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/kernel.h +68 -6
  26. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/kernel_utils.h +21 -155
  27. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/cactus +0 -0
  28. package/lib/module/classes/CactusLM.js +16 -49
  29. package/lib/module/classes/CactusLM.js.map +1 -1
  30. package/lib/module/classes/CactusSTT.js +30 -79
  31. package/lib/module/classes/CactusSTT.js.map +1 -1
  32. package/lib/module/classes/CactusVAD.js +95 -0
  33. package/lib/module/classes/CactusVAD.js.map +1 -0
  34. package/lib/module/hooks/useCactusLM.js +10 -11
  35. package/lib/module/hooks/useCactusLM.js.map +1 -1
  36. package/lib/module/hooks/useCactusSTT.js +23 -62
  37. package/lib/module/hooks/useCactusSTT.js.map +1 -1
  38. package/lib/module/hooks/useCactusVAD.js +171 -0
  39. package/lib/module/hooks/useCactusVAD.js.map +1 -0
  40. package/lib/module/index.js +2 -3
  41. package/lib/module/index.js.map +1 -1
  42. package/lib/module/modelRegistry.js +52 -0
  43. package/lib/module/modelRegistry.js.map +1 -0
  44. package/lib/module/native/Cactus.js +85 -23
  45. package/lib/module/native/Cactus.js.map +1 -1
  46. package/lib/module/native/CactusIndex.js.map +1 -1
  47. package/lib/module/native/index.js +0 -3
  48. package/lib/module/native/index.js.map +1 -1
  49. package/lib/module/types/CactusVAD.js +4 -0
  50. package/lib/module/{specs/CactusUtil.nitro.js.map → types/CactusVAD.js.map} +1 -1
  51. package/lib/typescript/src/classes/CactusLM.d.ts +5 -7
  52. package/lib/typescript/src/classes/CactusLM.d.ts.map +1 -1
  53. package/lib/typescript/src/classes/CactusSTT.d.ts +8 -12
  54. package/lib/typescript/src/classes/CactusSTT.d.ts.map +1 -1
  55. package/lib/typescript/src/classes/CactusVAD.d.ts +20 -0
  56. package/lib/typescript/src/classes/CactusVAD.d.ts.map +1 -0
  57. package/lib/typescript/src/hooks/useCactusLM.d.ts +2 -2
  58. package/lib/typescript/src/hooks/useCactusLM.d.ts.map +1 -1
  59. package/lib/typescript/src/hooks/useCactusSTT.d.ts +6 -8
  60. package/lib/typescript/src/hooks/useCactusSTT.d.ts.map +1 -1
  61. package/lib/typescript/src/hooks/useCactusVAD.d.ts +15 -0
  62. package/lib/typescript/src/hooks/useCactusVAD.d.ts.map +1 -0
  63. package/lib/typescript/src/index.d.ts +7 -5
  64. package/lib/typescript/src/index.d.ts.map +1 -1
  65. package/lib/typescript/src/modelRegistry.d.ts +5 -0
  66. package/lib/typescript/src/modelRegistry.d.ts.map +1 -0
  67. package/lib/typescript/src/native/Cactus.d.ts +12 -11
  68. package/lib/typescript/src/native/Cactus.d.ts.map +1 -1
  69. package/lib/typescript/src/native/CactusIndex.d.ts +2 -2
  70. package/lib/typescript/src/native/CactusIndex.d.ts.map +1 -1
  71. package/lib/typescript/src/native/index.d.ts +0 -3
  72. package/lib/typescript/src/native/index.d.ts.map +1 -1
  73. package/lib/typescript/src/specs/Cactus.nitro.d.ts +6 -6
  74. package/lib/typescript/src/specs/Cactus.nitro.d.ts.map +1 -1
  75. package/lib/typescript/src/types/CactusIndex.d.ts +2 -2
  76. package/lib/typescript/src/types/CactusIndex.d.ts.map +1 -1
  77. package/lib/typescript/src/types/CactusLM.d.ts +19 -11
  78. package/lib/typescript/src/types/CactusLM.d.ts.map +1 -1
  79. package/lib/typescript/src/types/CactusSTT.d.ts +33 -12
  80. package/lib/typescript/src/types/CactusSTT.d.ts.map +1 -1
  81. package/lib/typescript/src/types/CactusVAD.d.ts +34 -0
  82. package/lib/typescript/src/types/CactusVAD.d.ts.map +1 -0
  83. package/lib/typescript/src/types/common.d.ts +1 -6
  84. package/lib/typescript/src/types/common.d.ts.map +1 -1
  85. package/nitro.json +0 -11
  86. package/nitrogen/generated/android/cactus+autolinking.cmake +0 -5
  87. package/nitrogen/generated/android/cactusOnLoad.cpp +0 -30
  88. package/nitrogen/generated/ios/Cactus-Swift-Cxx-Bridge.cpp +0 -50
  89. package/nitrogen/generated/ios/Cactus-Swift-Cxx-Bridge.hpp +9 -147
  90. package/nitrogen/generated/ios/Cactus-Swift-Cxx-Umbrella.hpp +0 -13
  91. package/nitrogen/generated/ios/CactusAutolinking.mm +0 -26
  92. package/nitrogen/generated/ios/CactusAutolinking.swift +0 -30
  93. package/nitrogen/generated/shared/c++/HybridCactusSpec.cpp +4 -4
  94. package/nitrogen/generated/shared/c++/HybridCactusSpec.hpp +6 -6
  95. package/package.json +3 -3
  96. package/src/classes/CactusLM.ts +18 -65
  97. package/src/classes/CactusSTT.ts +39 -97
  98. package/src/classes/CactusVAD.ts +129 -0
  99. package/src/hooks/useCactusLM.ts +14 -17
  100. package/src/hooks/useCactusSTT.ts +47 -98
  101. package/src/hooks/useCactusVAD.ts +215 -0
  102. package/src/index.tsx +18 -12
  103. package/src/modelRegistry.ts +65 -0
  104. package/src/native/Cactus.ts +102 -41
  105. package/src/native/CactusIndex.ts +2 -2
  106. package/src/native/index.ts +0 -3
  107. package/src/specs/Cactus.nitro.ts +11 -7
  108. package/src/types/CactusIndex.ts +2 -2
  109. package/src/types/CactusLM.ts +19 -11
  110. package/src/types/CactusSTT.ts +33 -13
  111. package/src/types/CactusVAD.ts +39 -0
  112. package/src/types/common.ts +1 -6
  113. package/android/src/main/java/com/margelo/nitro/cactus/HybridCactusCrypto.kt +0 -46
  114. package/android/src/main/java/com/margelo/nitro/cactus/HybridCactusDeviceInfo.kt +0 -27
  115. package/android/src/main/jniLibs/arm64-v8a/libcactus_util.a +0 -0
  116. package/cpp/HybridCactusUtil.cpp +0 -47
  117. package/cpp/HybridCactusUtil.hpp +0 -27
  118. package/cpp/cactus_util.h +0 -25
  119. package/ios/HybridCactusCrypto.swift +0 -37
  120. package/ios/HybridCactusDeviceInfo.swift +0 -32
  121. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus_telemetry.h +0 -656
  122. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus_telemetry.h +0 -656
  123. package/ios/cactus_util.xcframework/Info.plist +0 -39
  124. package/ios/cactus_util.xcframework/ios-arm64/cactus_util.framework/Headers/cactus_util.h +0 -25
  125. package/ios/cactus_util.xcframework/ios-arm64/cactus_util.framework/Headers/database.h +0 -27
  126. package/ios/cactus_util.xcframework/ios-arm64/cactus_util.framework/Headers/ios_utils.h +0 -10
  127. package/ios/cactus_util.xcframework/ios-arm64/cactus_util.framework/Headers/logging.h +0 -25
  128. package/ios/cactus_util.xcframework/ios-arm64/cactus_util.framework/Info.plist +0 -0
  129. package/ios/cactus_util.xcframework/ios-arm64/cactus_util.framework/cactus_util +0 -0
  130. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/Headers/cactus_util.h +0 -25
  131. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/Headers/database.h +0 -27
  132. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/Headers/ios_utils.h +0 -10
  133. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/Headers/logging.h +0 -25
  134. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/Info.plist +0 -0
  135. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/_CodeSignature/CodeResources +0 -135
  136. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/cactus_util +0 -0
  137. package/lib/module/api/Database.js +0 -45
  138. package/lib/module/api/Database.js.map +0 -1
  139. package/lib/module/api/RemoteLM.js +0 -201
  140. package/lib/module/api/RemoteLM.js.map +0 -1
  141. package/lib/module/config/CactusConfig.js +0 -12
  142. package/lib/module/config/CactusConfig.js.map +0 -1
  143. package/lib/module/models.js +0 -336
  144. package/lib/module/models.js.map +0 -1
  145. package/lib/module/native/CactusCrypto.js +0 -10
  146. package/lib/module/native/CactusCrypto.js.map +0 -1
  147. package/lib/module/native/CactusDeviceInfo.js +0 -13
  148. package/lib/module/native/CactusDeviceInfo.js.map +0 -1
  149. package/lib/module/native/CactusUtil.js +0 -36
  150. package/lib/module/native/CactusUtil.js.map +0 -1
  151. package/lib/module/specs/CactusCrypto.nitro.js +0 -4
  152. package/lib/module/specs/CactusCrypto.nitro.js.map +0 -1
  153. package/lib/module/specs/CactusDeviceInfo.nitro.js +0 -4
  154. package/lib/module/specs/CactusDeviceInfo.nitro.js.map +0 -1
  155. package/lib/module/specs/CactusUtil.nitro.js +0 -4
  156. package/lib/module/telemetry/Telemetry.js +0 -154
  157. package/lib/module/telemetry/Telemetry.js.map +0 -1
  158. package/lib/typescript/src/api/Database.d.ts +0 -12
  159. package/lib/typescript/src/api/Database.d.ts.map +0 -1
  160. package/lib/typescript/src/api/RemoteLM.d.ts +0 -14
  161. package/lib/typescript/src/api/RemoteLM.d.ts.map +0 -1
  162. package/lib/typescript/src/config/CactusConfig.d.ts +0 -7
  163. package/lib/typescript/src/config/CactusConfig.d.ts.map +0 -1
  164. package/lib/typescript/src/models.d.ts +0 -6
  165. package/lib/typescript/src/models.d.ts.map +0 -1
  166. package/lib/typescript/src/native/CactusCrypto.d.ts +0 -5
  167. package/lib/typescript/src/native/CactusCrypto.d.ts.map +0 -1
  168. package/lib/typescript/src/native/CactusDeviceInfo.d.ts +0 -7
  169. package/lib/typescript/src/native/CactusDeviceInfo.d.ts.map +0 -1
  170. package/lib/typescript/src/native/CactusUtil.d.ts +0 -6
  171. package/lib/typescript/src/native/CactusUtil.d.ts.map +0 -1
  172. package/lib/typescript/src/specs/CactusCrypto.nitro.d.ts +0 -8
  173. package/lib/typescript/src/specs/CactusCrypto.nitro.d.ts.map +0 -1
  174. package/lib/typescript/src/specs/CactusDeviceInfo.nitro.d.ts +0 -16
  175. package/lib/typescript/src/specs/CactusDeviceInfo.nitro.d.ts.map +0 -1
  176. package/lib/typescript/src/specs/CactusUtil.nitro.d.ts +0 -10
  177. package/lib/typescript/src/specs/CactusUtil.nitro.d.ts.map +0 -1
  178. package/lib/typescript/src/telemetry/Telemetry.d.ts +0 -34
  179. package/lib/typescript/src/telemetry/Telemetry.d.ts.map +0 -1
  180. package/nitrogen/generated/android/c++/JDeviceInfo.hpp +0 -74
  181. package/nitrogen/generated/android/c++/JHybridCactusCryptoSpec.cpp +0 -65
  182. package/nitrogen/generated/android/c++/JHybridCactusCryptoSpec.hpp +0 -65
  183. package/nitrogen/generated/android/c++/JHybridCactusDeviceInfoSpec.cpp +0 -85
  184. package/nitrogen/generated/android/c++/JHybridCactusDeviceInfoSpec.hpp +0 -66
  185. package/nitrogen/generated/android/kotlin/com/margelo/nitro/cactus/DeviceInfo.kt +0 -50
  186. package/nitrogen/generated/android/kotlin/com/margelo/nitro/cactus/HybridCactusCryptoSpec.kt +0 -58
  187. package/nitrogen/generated/android/kotlin/com/margelo/nitro/cactus/HybridCactusDeviceInfoSpec.kt +0 -62
  188. package/nitrogen/generated/ios/c++/HybridCactusCryptoSpecSwift.cpp +0 -11
  189. package/nitrogen/generated/ios/c++/HybridCactusCryptoSpecSwift.hpp +0 -77
  190. package/nitrogen/generated/ios/c++/HybridCactusDeviceInfoSpecSwift.cpp +0 -11
  191. package/nitrogen/generated/ios/c++/HybridCactusDeviceInfoSpecSwift.hpp +0 -88
  192. package/nitrogen/generated/ios/swift/DeviceInfo.swift +0 -98
  193. package/nitrogen/generated/ios/swift/Func_void_DeviceInfo.swift +0 -47
  194. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__string_.swift +0 -54
  195. package/nitrogen/generated/ios/swift/HybridCactusCryptoSpec.swift +0 -57
  196. package/nitrogen/generated/ios/swift/HybridCactusCryptoSpec_cxx.swift +0 -139
  197. package/nitrogen/generated/ios/swift/HybridCactusDeviceInfoSpec.swift +0 -58
  198. package/nitrogen/generated/ios/swift/HybridCactusDeviceInfoSpec_cxx.swift +0 -164
  199. package/nitrogen/generated/shared/c++/DeviceInfo.hpp +0 -92
  200. package/nitrogen/generated/shared/c++/HybridCactusCryptoSpec.cpp +0 -21
  201. package/nitrogen/generated/shared/c++/HybridCactusCryptoSpec.hpp +0 -63
  202. package/nitrogen/generated/shared/c++/HybridCactusDeviceInfoSpec.cpp +0 -22
  203. package/nitrogen/generated/shared/c++/HybridCactusDeviceInfoSpec.hpp +0 -67
  204. package/nitrogen/generated/shared/c++/HybridCactusUtilSpec.cpp +0 -23
  205. package/nitrogen/generated/shared/c++/HybridCactusUtilSpec.hpp +0 -66
  206. package/src/api/Database.ts +0 -55
  207. package/src/api/RemoteLM.ts +0 -273
  208. package/src/config/CactusConfig.ts +0 -11
  209. package/src/models.ts +0 -344
  210. package/src/native/CactusCrypto.ts +0 -11
  211. package/src/native/CactusDeviceInfo.ts +0 -18
  212. package/src/native/CactusUtil.ts +0 -43
  213. package/src/specs/CactusCrypto.nitro.ts +0 -6
  214. package/src/specs/CactusDeviceInfo.nitro.ts +0 -15
  215. package/src/specs/CactusUtil.nitro.ts +0 -8
  216. package/src/telemetry/Telemetry.ts +0 -236
@@ -6,22 +6,18 @@ import type {
6
6
  CactusSTTParams,
7
7
  CactusSTTAudioEmbedParams,
8
8
  CactusSTTAudioEmbedResult,
9
- CactusSTTStreamTranscribeInsertParams,
9
+ CactusSTTStreamTranscribeStartOptions,
10
10
  CactusSTTStreamTranscribeProcessParams,
11
11
  CactusSTTStreamTranscribeProcessResult,
12
- CactusSTTStreamTranscribeFinalizeResult,
12
+ CactusSTTStreamTranscribeStopResult,
13
13
  } from '../types/CactusSTT';
14
- import { Telemetry } from '../telemetry/Telemetry';
15
- import { CactusConfig } from '../config/CactusConfig';
16
- import { getErrorMessage } from '../utils/error';
17
- import models from '../models';
14
+ import { getRegistry } from '../modelRegistry';
18
15
  import type { CactusModel } from '../types/common';
19
16
 
20
17
  export class CactusSTT {
21
18
  private readonly cactus = new Cactus();
22
19
 
23
20
  private readonly model: string;
24
- private readonly contextSize: number;
25
21
  private readonly options: {
26
22
  quantization: 'int4' | 'int8';
27
23
  pro: boolean;
@@ -30,13 +26,11 @@ export class CactusSTT {
30
26
  private isDownloading = false;
31
27
  private isInitialized = false;
32
28
  private isGenerating = false;
33
-
34
- private isStreamTranscribeInitialized = false;
29
+ private isStreamTranscribing = false;
35
30
 
36
31
  private static readonly defaultModel = 'whisper-small';
37
- private static readonly defaultContextSize = 2048;
38
32
  private static readonly defaultOptions = {
39
- quantization: 'int4' as const,
33
+ quantization: 'int8' as const,
40
34
  pro: false,
41
35
  };
42
36
  private static readonly defaultPrompt =
@@ -46,11 +40,8 @@ export class CactusSTT {
46
40
  };
47
41
  private static readonly defaultEmbedBufferSize = 4096;
48
42
 
49
- constructor({ model, contextSize, options }: CactusSTTParams = {}) {
50
- Telemetry.init(CactusConfig.telemetryToken);
51
-
43
+ constructor({ model, options }: CactusSTTParams = {}) {
52
44
  this.model = model ?? CactusSTT.defaultModel;
53
- this.contextSize = contextSize ?? CactusSTT.defaultContextSize;
54
45
  this.options = {
55
46
  quantization:
56
47
  options?.quantization ?? CactusSTT.defaultOptions.quantization,
@@ -78,8 +69,9 @@ export class CactusSTT {
78
69
 
79
70
  this.isDownloading = true;
80
71
  try {
72
+ const registry = await getRegistry();
81
73
  const modelConfig =
82
- models[this.model]?.quantization[this.options.quantization];
74
+ registry[this.model]?.quantization[this.options.quantization];
83
75
  const url = this.options.pro ? modelConfig?.pro?.apple : modelConfig?.url;
84
76
 
85
77
  if (!url) {
@@ -114,14 +106,10 @@ export class CactusSTT {
114
106
  modelPath = await CactusFileSystem.getModelPath(this.getModelName());
115
107
  }
116
108
 
117
- try {
118
- await this.cactus.init(modelPath, this.contextSize);
119
- Telemetry.logInit(this.model, true);
120
- this.isInitialized = true;
121
- } catch (error) {
122
- Telemetry.logInit(this.model, false, getErrorMessage(error));
123
- throw error;
124
- }
109
+ const cacheDir = await CactusFileSystem.getCactusDirectory();
110
+ await this.cactus.setTelemetryEnvironment(cacheDir);
111
+ await this.cactus.init(modelPath);
112
+ this.isInitialized = true;
125
113
  }
126
114
 
127
115
  public async transcribe({
@@ -145,95 +133,49 @@ export class CactusSTT {
145
133
 
146
134
  this.isGenerating = true;
147
135
  try {
148
- const result = await this.cactus.transcribe(
136
+ return await this.cactus.transcribe(
149
137
  audio,
150
138
  prompt,
151
139
  responseBufferSize,
152
140
  options,
153
141
  onToken
154
142
  );
155
- Telemetry.logTranscribe(
156
- this.model,
157
- result.success,
158
- result.success ? undefined : result.response,
159
- result
160
- );
161
- return result;
162
- } catch (error) {
163
- Telemetry.logTranscribe(this.model, false, getErrorMessage(error));
164
- throw error;
165
143
  } finally {
166
144
  this.isGenerating = false;
167
145
  }
168
146
  }
169
147
 
170
- public async streamTranscribeInit(): Promise<void> {
171
- if (this.isStreamTranscribeInitialized) {
148
+ public async streamTranscribeStart(
149
+ options?: CactusSTTStreamTranscribeStartOptions
150
+ ): Promise<void> {
151
+ if (this.isStreamTranscribing) {
172
152
  return;
173
153
  }
174
154
 
175
155
  await this.init();
176
-
177
- try {
178
- await this.cactus.streamTranscribeInit();
179
- this.isStreamTranscribeInitialized = true;
180
- } catch (error) {
181
- throw error;
182
- }
183
- }
184
-
185
- public async streamTranscribeInsert({
186
- audio,
187
- }: CactusSTTStreamTranscribeInsertParams): Promise<void> {
188
- if (!this.isStreamTranscribeInitialized) {
189
- throw new Error('CactusSTT stream transcribe is not initialized');
190
- }
191
-
192
- try {
193
- await this.cactus.streamTranscribeInsert(audio);
194
- } catch (error) {
195
- throw error;
196
- }
156
+ await this.cactus.streamTranscribeStart(options);
157
+ this.isStreamTranscribing = true;
197
158
  }
198
159
 
199
160
  public async streamTranscribeProcess({
200
- options,
201
- }: CactusSTTStreamTranscribeProcessParams = {}): Promise<CactusSTTStreamTranscribeProcessResult> {
202
- if (!this.isStreamTranscribeInitialized) {
203
- throw new Error('CactusSTT stream transcribe is not initialized');
161
+ audio,
162
+ }: CactusSTTStreamTranscribeProcessParams): Promise<CactusSTTStreamTranscribeProcessResult> {
163
+ if (!this.isStreamTranscribing) {
164
+ throw new Error('CactusSTT stream transcribe is not started');
204
165
  }
205
166
 
206
- try {
207
- const result = await this.cactus.streamTranscribeProcess(options);
208
- return result;
209
- } catch (error) {
210
- throw error;
211
- }
167
+ return this.cactus.streamTranscribeProcess(audio);
212
168
  }
213
169
 
214
- public async streamTranscribeFinalize(): Promise<CactusSTTStreamTranscribeFinalizeResult> {
215
- if (!this.isStreamTranscribeInitialized) {
216
- throw new Error('CactusSTT stream transcribe is not initialized');
170
+ public async streamTranscribeStop(): Promise<CactusSTTStreamTranscribeStopResult> {
171
+ if (!this.isStreamTranscribing) {
172
+ throw new Error('CactusSTT stream transcribe is not started');
217
173
  }
218
174
 
219
175
  try {
220
- const result = await this.cactus.streamTranscribeFinalize();
221
- return result;
222
- } catch (error) {
223
- throw error;
224
- }
225
- }
226
-
227
- public async streamTranscribeDestroy(): Promise<void> {
228
- if (!this.isStreamTranscribeInitialized) {
229
- return;
230
- }
231
-
232
- try {
233
- await this.cactus.streamTranscribeDestroy();
234
- this.isStreamTranscribeInitialized = false;
235
- } catch (error) {
236
- throw error;
176
+ return await this.cactus.streamTranscribeStop();
177
+ } finally {
178
+ this.isStreamTranscribing = false;
237
179
  }
238
180
  }
239
181
 
@@ -252,11 +194,7 @@ export class CactusSTT {
252
194
  audioPath,
253
195
  CactusSTT.defaultEmbedBufferSize
254
196
  );
255
- Telemetry.logAudioEmbedding(this.model, true);
256
197
  return { embedding };
257
- } catch (error) {
258
- Telemetry.logAudioEmbedding(this.model, false, getErrorMessage(error));
259
- throw error;
260
198
  } finally {
261
199
  this.isGenerating = false;
262
200
  }
@@ -277,21 +215,25 @@ export class CactusSTT {
277
215
  }
278
216
 
279
217
  await this.stop();
280
- await this.streamTranscribeDestroy();
281
- await this.cactus.destroy();
282
218
 
219
+ if (this.isStreamTranscribing) {
220
+ await this.cactus.streamTranscribeStop().catch(() => {});
221
+ this.isStreamTranscribing = false;
222
+ }
223
+
224
+ await this.cactus.destroy();
283
225
  this.isInitialized = false;
284
226
  }
285
227
 
286
- public getModels(): CactusModel[] {
287
- return Object.values(models).filter((model) => model.speech);
228
+ public async getModels(): Promise<CactusModel[]> {
229
+ return Object.values(await getRegistry());
288
230
  }
289
231
 
290
232
  private isModelPath(model: string): boolean {
291
233
  return model.startsWith('file://') || model.startsWith('/');
292
234
  }
293
235
 
294
- private getModelName(): string {
236
+ public getModelName(): string {
295
237
  return `${this.model}-${this.options.quantization}${this.options.pro ? '-pro' : ''}`;
296
238
  }
297
239
  }
@@ -0,0 +1,129 @@
1
+ import { Cactus, CactusFileSystem } from '../native';
2
+ import type {
3
+ CactusVADParams,
4
+ CactusVADDownloadParams,
5
+ CactusVADVadParams,
6
+ CactusVADResult,
7
+ } from '../types/CactusVAD';
8
+ import { getRegistry } from '../modelRegistry';
9
+ import type { CactusModel } from '../types/common';
10
+
11
+ export class CactusVAD {
12
+ private readonly cactus = new Cactus();
13
+
14
+ private readonly model: string;
15
+ private readonly options: {
16
+ quantization: 'int4' | 'int8';
17
+ pro: boolean;
18
+ };
19
+
20
+ private isDownloading = false;
21
+ private isInitialized = false;
22
+
23
+ private static readonly defaultModel = 'silero-vad';
24
+ private static readonly defaultOptions = {
25
+ quantization: 'int8' as const,
26
+ pro: false,
27
+ };
28
+
29
+ constructor({ model, options }: CactusVADParams = {}) {
30
+ this.model = model ?? CactusVAD.defaultModel;
31
+ this.options = {
32
+ quantization:
33
+ options?.quantization ?? CactusVAD.defaultOptions.quantization,
34
+ pro: options?.pro ?? CactusVAD.defaultOptions.pro,
35
+ };
36
+ }
37
+
38
+ public async download({
39
+ onProgress,
40
+ }: CactusVADDownloadParams = {}): Promise<void> {
41
+ if (this.isModelPath(this.model)) {
42
+ onProgress?.(1.0);
43
+ return;
44
+ }
45
+
46
+ if (this.isDownloading) {
47
+ throw new Error('CactusVAD is already downloading');
48
+ }
49
+
50
+ if (await CactusFileSystem.modelExists(this.getModelName())) {
51
+ console.log('Model already exists', this.getModelName());
52
+ onProgress?.(1.0);
53
+ return;
54
+ }
55
+
56
+ this.isDownloading = true;
57
+ try {
58
+ const registry = await getRegistry();
59
+ const modelConfig =
60
+ registry[this.model]?.quantization[this.options.quantization];
61
+ const url = this.options.pro ? modelConfig?.pro?.apple : modelConfig?.url;
62
+
63
+ if (!url) {
64
+ throw new Error(`Model ${this.model} with specified options not found`);
65
+ }
66
+
67
+ await CactusFileSystem.downloadModel(
68
+ this.getModelName(),
69
+ url,
70
+ onProgress
71
+ );
72
+ } finally {
73
+ this.isDownloading = false;
74
+ }
75
+ }
76
+
77
+ public async init(): Promise<void> {
78
+ if (this.isInitialized) {
79
+ return;
80
+ }
81
+
82
+ let modelPath: string;
83
+ if (this.isModelPath(this.model)) {
84
+ modelPath = this.model.replace('file://', '');
85
+ } else {
86
+ if (!(await CactusFileSystem.modelExists(this.getModelName()))) {
87
+ console.log('Model does not exist', this.getModelName());
88
+ throw new Error(
89
+ `Model "${this.model}" with options ${JSON.stringify(this.options)} is not downloaded`
90
+ );
91
+ }
92
+ modelPath = await CactusFileSystem.getModelPath(this.getModelName());
93
+ }
94
+
95
+ const cacheDir = await CactusFileSystem.getCactusDirectory();
96
+ await this.cactus.setTelemetryEnvironment(cacheDir);
97
+ await this.cactus.init(modelPath);
98
+ this.isInitialized = true;
99
+ }
100
+
101
+ public async vad({
102
+ audio,
103
+ options,
104
+ }: CactusVADVadParams): Promise<CactusVADResult> {
105
+ await this.init();
106
+ return this.cactus.vad(audio, options);
107
+ }
108
+
109
+ public async destroy(): Promise<void> {
110
+ if (!this.isInitialized) {
111
+ return;
112
+ }
113
+
114
+ await this.cactus.destroy();
115
+ this.isInitialized = false;
116
+ }
117
+
118
+ public async getModels(): Promise<CactusModel[]> {
119
+ return Object.values(await getRegistry());
120
+ }
121
+
122
+ private isModelPath(model: string): boolean {
123
+ return model.startsWith('file://') || model.startsWith('/');
124
+ }
125
+
126
+ public getModelName(): string {
127
+ return `${this.model}-${this.options.quantization}${this.options.pro ? '-pro' : ''}`;
128
+ }
129
+ }
@@ -20,15 +20,15 @@ import type { CactusModel } from '../types/common';
20
20
 
21
21
  export const useCactusLM = ({
22
22
  model = 'qwen3-0.6b',
23
- contextSize = 2048,
24
23
  corpusDir = undefined,
24
+ cacheIndex = false,
25
25
  options: modelOptions = {
26
26
  quantization: undefined,
27
27
  pro: false,
28
28
  },
29
29
  }: CactusLMParams = {}) => {
30
30
  const [cactusLM, setCactusLM] = useState(
31
- () => new CactusLM({ model, contextSize, corpusDir, options: modelOptions })
31
+ () => new CactusLM({ model, corpusDir, cacheIndex, options: modelOptions })
32
32
  );
33
33
 
34
34
  // State
@@ -48,17 +48,16 @@ export const useCactusLM = ({
48
48
  }, [model]);
49
49
 
50
50
  useEffect(() => {
51
- setCactusLM(
52
- new CactusLM({
53
- model,
54
- contextSize,
55
- corpusDir,
56
- options: {
57
- quantization: modelOptions.quantization,
58
- pro: modelOptions.pro,
59
- },
60
- })
61
- );
51
+ const newInstance = new CactusLM({
52
+ model,
53
+ corpusDir,
54
+ cacheIndex,
55
+ options: {
56
+ quantization: modelOptions.quantization,
57
+ pro: modelOptions.pro,
58
+ },
59
+ });
60
+ setCactusLM(newInstance);
62
61
 
63
62
  setCompletion('');
64
63
  setIsGenerating(false);
@@ -69,7 +68,7 @@ export const useCactusLM = ({
69
68
  setError(null);
70
69
 
71
70
  let mounted = true;
72
- CactusFileSystem.modelExists(model)
71
+ CactusFileSystem.modelExists(newInstance.getModelName())
73
72
  .then((exists) => {
74
73
  if (!mounted) {
75
74
  return;
@@ -89,8 +88,8 @@ export const useCactusLM = ({
89
88
  };
90
89
  }, [
91
90
  model,
92
- contextSize,
93
91
  corpusDir,
92
+ cacheIndex,
94
93
  modelOptions.quantization,
95
94
  modelOptions.pro,
96
95
  ]);
@@ -193,7 +192,6 @@ export const useCactusLM = ({
193
192
  options,
194
193
  tools,
195
194
  onToken,
196
- mode,
197
195
  }: CactusLMCompleteParams): Promise<CactusLMCompleteResult> => {
198
196
  if (isGenerating) {
199
197
  const message = 'CactusLM is already generating';
@@ -213,7 +211,6 @@ export const useCactusLM = ({
213
211
  setCompletion((prev) => prev + token);
214
212
  onToken?.(token);
215
213
  },
216
- mode,
217
214
  });
218
215
  } catch (e) {
219
216
  setError(getErrorMessage(e));
@@ -9,23 +9,22 @@ import type {
9
9
  CactusSTTDownloadParams,
10
10
  CactusSTTAudioEmbedParams,
11
11
  CactusSTTAudioEmbedResult,
12
- CactusSTTStreamTranscribeInsertParams,
12
+ CactusSTTStreamTranscribeStartOptions,
13
13
  CactusSTTStreamTranscribeProcessParams,
14
14
  CactusSTTStreamTranscribeProcessResult,
15
- CactusSTTStreamTranscribeFinalizeResult,
15
+ CactusSTTStreamTranscribeStopResult,
16
16
  } from '../types/CactusSTT';
17
17
  import type { CactusModel } from '../types/common';
18
18
 
19
19
  export const useCactusSTT = ({
20
20
  model = 'whisper-small',
21
- contextSize = 2048,
22
21
  options: modelOptions = {
23
22
  quantization: undefined,
24
23
  pro: false,
25
24
  },
26
25
  }: CactusSTTParams = {}) => {
27
26
  const [cactusSTT, setCactusSTT] = useState(
28
- () => new CactusSTT({ model, contextSize, options: modelOptions })
27
+ () => new CactusSTT({ model, options: modelOptions })
29
28
  );
30
29
 
31
30
  // State
@@ -49,16 +48,14 @@ export const useCactusSTT = ({
49
48
  }, [model]);
50
49
 
51
50
  useEffect(() => {
52
- setCactusSTT(
53
- new CactusSTT({
54
- model,
55
- contextSize,
56
- options: {
57
- quantization: modelOptions.quantization,
58
- pro: modelOptions.pro,
59
- },
60
- })
61
- );
51
+ const newInstance = new CactusSTT({
52
+ model,
53
+ options: {
54
+ quantization: modelOptions.quantization,
55
+ pro: modelOptions.pro,
56
+ },
57
+ });
58
+ setCactusSTT(newInstance);
62
59
 
63
60
  setTranscription('');
64
61
  setStreamTranscribeConfirmed('');
@@ -72,25 +69,21 @@ export const useCactusSTT = ({
72
69
  setError(null);
73
70
 
74
71
  let mounted = true;
75
- CactusFileSystem.modelExists(model)
72
+ CactusFileSystem.modelExists(newInstance.getModelName())
76
73
  .then((exists) => {
77
- if (!mounted) {
78
- return;
79
- }
80
- setIsDownloaded(exists);
74
+ if (mounted) setIsDownloaded(exists);
81
75
  })
82
76
  .catch((e) => {
83
- if (!mounted) {
84
- return;
77
+ if (mounted) {
78
+ setIsDownloaded(false);
79
+ setError(getErrorMessage(e));
85
80
  }
86
- setIsDownloaded(false);
87
- setError(getErrorMessage(e));
88
81
  });
89
82
 
90
83
  return () => {
91
84
  mounted = false;
92
85
  };
93
- }, [model, contextSize, modelOptions.quantization, modelOptions.pro]);
86
+ }, [model, modelOptions.quantization, modelOptions.pro]);
94
87
 
95
88
  useEffect(() => {
96
89
  return () => {
@@ -115,49 +108,29 @@ export const useCactusSTT = ({
115
108
  const thisModel = currentModelRef.current;
116
109
  const thisDownloadId = ++currentDownloadIdRef.current;
117
110
 
111
+ const isCurrent = () =>
112
+ currentModelRef.current === thisModel &&
113
+ currentDownloadIdRef.current === thisDownloadId;
114
+
118
115
  setDownloadProgress(0);
119
116
  setIsDownloading(true);
120
117
  try {
121
118
  await cactusSTT.download({
122
119
  onProgress: (progress) => {
123
- if (
124
- currentModelRef.current !== thisModel ||
125
- currentDownloadIdRef.current !== thisDownloadId
126
- ) {
127
- return;
128
- }
129
-
120
+ if (!isCurrent()) return;
130
121
  setDownloadProgress(progress);
131
122
  onProgress?.(progress);
132
123
  },
133
124
  });
134
125
 
135
- if (
136
- currentModelRef.current !== thisModel ||
137
- currentDownloadIdRef.current !== thisDownloadId
138
- ) {
139
- return;
140
- }
141
-
126
+ if (!isCurrent()) return;
142
127
  setIsDownloaded(true);
143
128
  } catch (e) {
144
- if (
145
- currentModelRef.current !== thisModel ||
146
- currentDownloadIdRef.current !== thisDownloadId
147
- ) {
148
- return;
149
- }
150
-
129
+ if (!isCurrent()) return;
151
130
  setError(getErrorMessage(e));
152
131
  throw e;
153
132
  } finally {
154
- if (
155
- currentModelRef.current !== thisModel ||
156
- currentDownloadIdRef.current !== thisDownloadId
157
- ) {
158
- return;
159
- }
160
-
133
+ if (!isCurrent()) return;
161
134
  setIsDownloading(false);
162
135
  setDownloadProgress(0);
163
136
  }
@@ -244,44 +217,34 @@ export const useCactusSTT = ({
244
217
  [cactusSTT, isGenerating]
245
218
  );
246
219
 
247
- const streamTranscribeInit = useCallback(async () => {
248
- if (isStreamTranscribing) {
249
- return;
250
- }
251
-
252
- setError(null);
253
- setStreamTranscribeConfirmed('');
254
- setStreamTranscribePending('');
255
- setIsStreamTranscribing(true);
256
- try {
257
- await cactusSTT.streamTranscribeInit();
258
- } catch (e) {
259
- setError(getErrorMessage(e));
260
- setIsStreamTranscribing(false);
261
- throw e;
262
- }
263
- }, [cactusSTT, isStreamTranscribing]);
220
+ const streamTranscribeStart = useCallback(
221
+ async (options?: CactusSTTStreamTranscribeStartOptions) => {
222
+ if (isStreamTranscribing) {
223
+ return;
224
+ }
264
225
 
265
- const streamTranscribeInsert = useCallback(
266
- async ({ audio }: CactusSTTStreamTranscribeInsertParams): Promise<void> => {
267
226
  setError(null);
227
+ setStreamTranscribeConfirmed('');
228
+ setStreamTranscribePending('');
229
+ setIsStreamTranscribing(true);
268
230
  try {
269
- await cactusSTT.streamTranscribeInsert({ audio });
231
+ await cactusSTT.streamTranscribeStart(options);
270
232
  } catch (e) {
271
233
  setError(getErrorMessage(e));
234
+ setIsStreamTranscribing(false);
272
235
  throw e;
273
236
  }
274
237
  },
275
- [cactusSTT]
238
+ [cactusSTT, isStreamTranscribing]
276
239
  );
277
240
 
278
241
  const streamTranscribeProcess = useCallback(
279
242
  async ({
280
- options,
281
- }: CactusSTTStreamTranscribeProcessParams = {}): Promise<CactusSTTStreamTranscribeProcessResult> => {
243
+ audio,
244
+ }: CactusSTTStreamTranscribeProcessParams): Promise<CactusSTTStreamTranscribeProcessResult> => {
282
245
  setError(null);
283
246
  try {
284
- const result = await cactusSTT.streamTranscribeProcess({ options });
247
+ const result = await cactusSTT.streamTranscribeProcess({ audio });
285
248
  setStreamTranscribeConfirmed((prev) => prev + result.confirmed);
286
249
  setStreamTranscribePending(result.pending);
287
250
  return result;
@@ -293,34 +256,22 @@ export const useCactusSTT = ({
293
256
  [cactusSTT]
294
257
  );
295
258
 
296
- const streamTranscribeFinalize =
297
- useCallback(async (): Promise<CactusSTTStreamTranscribeFinalizeResult> => {
259
+ const streamTranscribeStop =
260
+ useCallback(async (): Promise<CactusSTTStreamTranscribeStopResult> => {
298
261
  setError(null);
299
262
  try {
300
- const result = await cactusSTT.streamTranscribeFinalize();
263
+ const result = await cactusSTT.streamTranscribeStop();
301
264
  setStreamTranscribeConfirmed((prev) => prev + result.confirmed);
302
265
  setStreamTranscribePending('');
303
- setIsStreamTranscribing(false);
304
266
  return result;
305
267
  } catch (e) {
306
268
  setError(getErrorMessage(e));
307
269
  throw e;
270
+ } finally {
271
+ setIsStreamTranscribing(false);
308
272
  }
309
273
  }, [cactusSTT]);
310
274
 
311
- const streamTranscribeDestroy = useCallback(async (): Promise<void> => {
312
- setError(null);
313
- try {
314
- await cactusSTT.streamTranscribeDestroy();
315
- } catch (e) {
316
- setError(getErrorMessage(e));
317
- throw e;
318
- } finally {
319
- setIsStreamTranscribing(false);
320
- setStreamTranscribePending('');
321
- }
322
- }, [cactusSTT]);
323
-
324
275
  const stop = useCallback(async () => {
325
276
  setError(null);
326
277
  try {
@@ -384,13 +335,11 @@ export const useCactusSTT = ({
384
335
  init,
385
336
  transcribe,
386
337
  audioEmbed,
387
- streamTranscribeInit,
388
- streamTranscribeInsert,
338
+ streamTranscribeStart,
389
339
  streamTranscribeProcess,
390
- streamTranscribeFinalize,
391
- streamTranscribeDestroy,
392
- reset,
340
+ streamTranscribeStop,
393
341
  stop,
342
+ reset,
394
343
  destroy,
395
344
  getModels,
396
345
  };