cactus-react-native 1.4.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 (226) hide show
  1. package/Cactus.podspec +1 -1
  2. package/README.md +465 -174
  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 +157 -6
  10. package/cpp/HybridCactus.hpp +20 -3
  11. package/cpp/cactus_ffi.h +65 -30
  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 +65 -30
  14. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus_utils.h +357 -122
  15. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/engine.h +184 -63
  16. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/gemma_tools.h +549 -0
  17. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/graph.h +153 -27
  18. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/kernel.h +90 -178
  19. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/kernel_utils.h +276 -151
  20. package/ios/cactus.xcframework/ios-arm64/cactus.framework/cactus +0 -0
  21. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus.h +0 -1
  22. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus_ffi.h +65 -30
  23. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus_utils.h +357 -122
  24. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/engine.h +184 -63
  25. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/gemma_tools.h +549 -0
  26. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/graph.h +153 -27
  27. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/kernel.h +90 -178
  28. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/kernel_utils.h +276 -151
  29. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/cactus +0 -0
  30. package/lib/module/classes/CactusLM.js +43 -58
  31. package/lib/module/classes/CactusLM.js.map +1 -1
  32. package/lib/module/classes/CactusSTT.js +64 -38
  33. package/lib/module/classes/CactusSTT.js.map +1 -1
  34. package/lib/module/classes/CactusVAD.js +95 -0
  35. package/lib/module/classes/CactusVAD.js.map +1 -0
  36. package/lib/module/hooks/useCactusLM.js +23 -15
  37. package/lib/module/hooks/useCactusLM.js.map +1 -1
  38. package/lib/module/hooks/useCactusSTT.js +85 -28
  39. package/lib/module/hooks/useCactusSTT.js.map +1 -1
  40. package/lib/module/hooks/useCactusVAD.js +171 -0
  41. package/lib/module/hooks/useCactusVAD.js.map +1 -0
  42. package/lib/module/index.js +2 -3
  43. package/lib/module/index.js.map +1 -1
  44. package/lib/module/modelRegistry.js +52 -0
  45. package/lib/module/modelRegistry.js.map +1 -0
  46. package/lib/module/native/Cactus.js +107 -8
  47. package/lib/module/native/Cactus.js.map +1 -1
  48. package/lib/module/native/CactusIndex.js.map +1 -1
  49. package/lib/module/native/index.js +0 -3
  50. package/lib/module/native/index.js.map +1 -1
  51. package/lib/module/types/CactusLM.js +2 -0
  52. package/lib/module/types/CactusSTT.js +2 -0
  53. package/lib/module/types/CactusVAD.js +4 -0
  54. package/lib/module/types/{CactusModel.js.map → CactusVAD.js.map} +1 -1
  55. package/lib/module/types/common.js +2 -0
  56. package/lib/module/types/{CactusSTTModel.js.map → common.js.map} +1 -1
  57. package/lib/typescript/src/classes/CactusLM.d.ts +8 -6
  58. package/lib/typescript/src/classes/CactusLM.d.ts.map +1 -1
  59. package/lib/typescript/src/classes/CactusSTT.d.ts +11 -6
  60. package/lib/typescript/src/classes/CactusSTT.d.ts.map +1 -1
  61. package/lib/typescript/src/classes/CactusVAD.d.ts +20 -0
  62. package/lib/typescript/src/classes/CactusVAD.d.ts.map +1 -0
  63. package/lib/typescript/src/hooks/useCactusLM.d.ts +3 -3
  64. package/lib/typescript/src/hooks/useCactusLM.d.ts.map +1 -1
  65. package/lib/typescript/src/hooks/useCactusSTT.d.ts +11 -5
  66. package/lib/typescript/src/hooks/useCactusSTT.d.ts.map +1 -1
  67. package/lib/typescript/src/hooks/useCactusVAD.d.ts +15 -0
  68. package/lib/typescript/src/hooks/useCactusVAD.d.ts.map +1 -0
  69. package/lib/typescript/src/index.d.ts +7 -6
  70. package/lib/typescript/src/index.d.ts.map +1 -1
  71. package/lib/typescript/src/modelRegistry.d.ts +5 -0
  72. package/lib/typescript/src/modelRegistry.d.ts.map +1 -0
  73. package/lib/typescript/src/native/Cactus.d.ts +12 -6
  74. package/lib/typescript/src/native/Cactus.d.ts.map +1 -1
  75. package/lib/typescript/src/native/CactusIndex.d.ts +2 -2
  76. package/lib/typescript/src/native/CactusIndex.d.ts.map +1 -1
  77. package/lib/typescript/src/native/index.d.ts +0 -3
  78. package/lib/typescript/src/native/index.d.ts.map +1 -1
  79. package/lib/typescript/src/specs/Cactus.nitro.d.ts +6 -1
  80. package/lib/typescript/src/specs/Cactus.nitro.d.ts.map +1 -1
  81. package/lib/typescript/src/types/CactusIndex.d.ts +2 -2
  82. package/lib/typescript/src/types/CactusIndex.d.ts.map +1 -1
  83. package/lib/typescript/src/types/CactusLM.d.ts +19 -9
  84. package/lib/typescript/src/types/CactusLM.d.ts.map +1 -1
  85. package/lib/typescript/src/types/CactusSTT.d.ts +45 -4
  86. package/lib/typescript/src/types/CactusSTT.d.ts.map +1 -1
  87. package/lib/typescript/src/types/CactusVAD.d.ts +34 -0
  88. package/lib/typescript/src/types/CactusVAD.d.ts.map +1 -0
  89. package/lib/typescript/src/types/common.d.ts +23 -0
  90. package/lib/typescript/src/types/common.d.ts.map +1 -0
  91. package/nitro.json +0 -11
  92. package/nitrogen/generated/android/cactus+autolinking.cmake +0 -5
  93. package/nitrogen/generated/android/cactusOnLoad.cpp +0 -30
  94. package/nitrogen/generated/ios/Cactus-Swift-Cxx-Bridge.cpp +0 -50
  95. package/nitrogen/generated/ios/Cactus-Swift-Cxx-Bridge.hpp +9 -147
  96. package/nitrogen/generated/ios/Cactus-Swift-Cxx-Umbrella.hpp +0 -13
  97. package/nitrogen/generated/ios/CactusAutolinking.mm +0 -26
  98. package/nitrogen/generated/ios/CactusAutolinking.swift +0 -30
  99. package/nitrogen/generated/shared/c++/HybridCactusSpec.cpp +5 -0
  100. package/nitrogen/generated/shared/c++/HybridCactusSpec.hpp +6 -1
  101. package/package.json +3 -3
  102. package/src/classes/CactusLM.ts +59 -74
  103. package/src/classes/CactusSTT.ts +92 -49
  104. package/src/classes/CactusVAD.ts +129 -0
  105. package/src/hooks/useCactusLM.ts +26 -9
  106. package/src/hooks/useCactusSTT.ts +105 -44
  107. package/src/hooks/useCactusVAD.ts +215 -0
  108. package/src/index.tsx +20 -10
  109. package/src/modelRegistry.ts +65 -0
  110. package/src/native/Cactus.ts +130 -14
  111. package/src/native/CactusIndex.ts +2 -2
  112. package/src/native/index.ts +0 -3
  113. package/src/specs/Cactus.nitro.ts +11 -2
  114. package/src/types/CactusIndex.ts +2 -2
  115. package/src/types/CactusLM.ts +20 -9
  116. package/src/types/CactusSTT.ts +50 -4
  117. package/src/types/CactusVAD.ts +39 -0
  118. package/src/types/common.ts +23 -0
  119. package/android/src/main/java/com/margelo/nitro/cactus/HybridCactusCrypto.kt +0 -46
  120. package/android/src/main/java/com/margelo/nitro/cactus/HybridCactusDeviceInfo.kt +0 -27
  121. package/android/src/main/jniLibs/arm64-v8a/libcactus_util.a +0 -0
  122. package/cpp/HybridCactusUtil.cpp +0 -47
  123. package/cpp/HybridCactusUtil.hpp +0 -27
  124. package/cpp/cactus_util.h +0 -25
  125. package/ios/HybridCactusCrypto.swift +0 -37
  126. package/ios/HybridCactusDeviceInfo.swift +0 -32
  127. package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus_telemetry.h +0 -656
  128. package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus_telemetry.h +0 -656
  129. package/ios/cactus_util.xcframework/Info.plist +0 -39
  130. package/ios/cactus_util.xcframework/ios-arm64/cactus_util.framework/Headers/cactus_util.h +0 -25
  131. package/ios/cactus_util.xcframework/ios-arm64/cactus_util.framework/Headers/database.h +0 -27
  132. package/ios/cactus_util.xcframework/ios-arm64/cactus_util.framework/Headers/ios_utils.h +0 -10
  133. package/ios/cactus_util.xcframework/ios-arm64/cactus_util.framework/Headers/logging.h +0 -25
  134. package/ios/cactus_util.xcframework/ios-arm64/cactus_util.framework/Info.plist +0 -0
  135. package/ios/cactus_util.xcframework/ios-arm64/cactus_util.framework/cactus_util +0 -0
  136. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/Headers/cactus_util.h +0 -25
  137. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/Headers/database.h +0 -27
  138. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/Headers/ios_utils.h +0 -10
  139. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/Headers/logging.h +0 -25
  140. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/Info.plist +0 -0
  141. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/_CodeSignature/CodeResources +0 -135
  142. package/ios/cactus_util.xcframework/ios-arm64-simulator/cactus_util.framework/cactus_util +0 -0
  143. package/lib/module/api/Database.js +0 -137
  144. package/lib/module/api/Database.js.map +0 -1
  145. package/lib/module/api/RemoteLM.js +0 -201
  146. package/lib/module/api/RemoteLM.js.map +0 -1
  147. package/lib/module/config/CactusConfig.js +0 -12
  148. package/lib/module/config/CactusConfig.js.map +0 -1
  149. package/lib/module/native/CactusCrypto.js +0 -10
  150. package/lib/module/native/CactusCrypto.js.map +0 -1
  151. package/lib/module/native/CactusDeviceInfo.js +0 -13
  152. package/lib/module/native/CactusDeviceInfo.js.map +0 -1
  153. package/lib/module/native/CactusUtil.js +0 -36
  154. package/lib/module/native/CactusUtil.js.map +0 -1
  155. package/lib/module/specs/CactusCrypto.nitro.js +0 -4
  156. package/lib/module/specs/CactusCrypto.nitro.js.map +0 -1
  157. package/lib/module/specs/CactusDeviceInfo.nitro.js +0 -4
  158. package/lib/module/specs/CactusDeviceInfo.nitro.js.map +0 -1
  159. package/lib/module/specs/CactusUtil.nitro.js +0 -4
  160. package/lib/module/specs/CactusUtil.nitro.js.map +0 -1
  161. package/lib/module/telemetry/Telemetry.js +0 -154
  162. package/lib/module/telemetry/Telemetry.js.map +0 -1
  163. package/lib/module/types/CactusModel.js +0 -2
  164. package/lib/module/types/CactusSTTModel.js +0 -2
  165. package/lib/typescript/src/api/Database.d.ts +0 -18
  166. package/lib/typescript/src/api/Database.d.ts.map +0 -1
  167. package/lib/typescript/src/api/RemoteLM.d.ts +0 -14
  168. package/lib/typescript/src/api/RemoteLM.d.ts.map +0 -1
  169. package/lib/typescript/src/config/CactusConfig.d.ts +0 -7
  170. package/lib/typescript/src/config/CactusConfig.d.ts.map +0 -1
  171. package/lib/typescript/src/native/CactusCrypto.d.ts +0 -5
  172. package/lib/typescript/src/native/CactusCrypto.d.ts.map +0 -1
  173. package/lib/typescript/src/native/CactusDeviceInfo.d.ts +0 -7
  174. package/lib/typescript/src/native/CactusDeviceInfo.d.ts.map +0 -1
  175. package/lib/typescript/src/native/CactusUtil.d.ts +0 -6
  176. package/lib/typescript/src/native/CactusUtil.d.ts.map +0 -1
  177. package/lib/typescript/src/specs/CactusCrypto.nitro.d.ts +0 -8
  178. package/lib/typescript/src/specs/CactusCrypto.nitro.d.ts.map +0 -1
  179. package/lib/typescript/src/specs/CactusDeviceInfo.nitro.d.ts +0 -16
  180. package/lib/typescript/src/specs/CactusDeviceInfo.nitro.d.ts.map +0 -1
  181. package/lib/typescript/src/specs/CactusUtil.nitro.d.ts +0 -10
  182. package/lib/typescript/src/specs/CactusUtil.nitro.d.ts.map +0 -1
  183. package/lib/typescript/src/telemetry/Telemetry.d.ts +0 -34
  184. package/lib/typescript/src/telemetry/Telemetry.d.ts.map +0 -1
  185. package/lib/typescript/src/types/CactusModel.d.ts +0 -13
  186. package/lib/typescript/src/types/CactusModel.d.ts.map +0 -1
  187. package/lib/typescript/src/types/CactusSTTModel.d.ts +0 -8
  188. package/lib/typescript/src/types/CactusSTTModel.d.ts.map +0 -1
  189. package/nitrogen/generated/android/c++/JDeviceInfo.hpp +0 -74
  190. package/nitrogen/generated/android/c++/JHybridCactusCryptoSpec.cpp +0 -65
  191. package/nitrogen/generated/android/c++/JHybridCactusCryptoSpec.hpp +0 -65
  192. package/nitrogen/generated/android/c++/JHybridCactusDeviceInfoSpec.cpp +0 -85
  193. package/nitrogen/generated/android/c++/JHybridCactusDeviceInfoSpec.hpp +0 -66
  194. package/nitrogen/generated/android/kotlin/com/margelo/nitro/cactus/DeviceInfo.kt +0 -50
  195. package/nitrogen/generated/android/kotlin/com/margelo/nitro/cactus/HybridCactusCryptoSpec.kt +0 -58
  196. package/nitrogen/generated/android/kotlin/com/margelo/nitro/cactus/HybridCactusDeviceInfoSpec.kt +0 -62
  197. package/nitrogen/generated/ios/c++/HybridCactusCryptoSpecSwift.cpp +0 -11
  198. package/nitrogen/generated/ios/c++/HybridCactusCryptoSpecSwift.hpp +0 -77
  199. package/nitrogen/generated/ios/c++/HybridCactusDeviceInfoSpecSwift.cpp +0 -11
  200. package/nitrogen/generated/ios/c++/HybridCactusDeviceInfoSpecSwift.hpp +0 -88
  201. package/nitrogen/generated/ios/swift/DeviceInfo.swift +0 -98
  202. package/nitrogen/generated/ios/swift/Func_void_DeviceInfo.swift +0 -47
  203. package/nitrogen/generated/ios/swift/Func_void_std__optional_std__string_.swift +0 -54
  204. package/nitrogen/generated/ios/swift/HybridCactusCryptoSpec.swift +0 -57
  205. package/nitrogen/generated/ios/swift/HybridCactusCryptoSpec_cxx.swift +0 -139
  206. package/nitrogen/generated/ios/swift/HybridCactusDeviceInfoSpec.swift +0 -58
  207. package/nitrogen/generated/ios/swift/HybridCactusDeviceInfoSpec_cxx.swift +0 -164
  208. package/nitrogen/generated/shared/c++/DeviceInfo.hpp +0 -92
  209. package/nitrogen/generated/shared/c++/HybridCactusCryptoSpec.cpp +0 -21
  210. package/nitrogen/generated/shared/c++/HybridCactusCryptoSpec.hpp +0 -63
  211. package/nitrogen/generated/shared/c++/HybridCactusDeviceInfoSpec.cpp +0 -22
  212. package/nitrogen/generated/shared/c++/HybridCactusDeviceInfoSpec.hpp +0 -67
  213. package/nitrogen/generated/shared/c++/HybridCactusUtilSpec.cpp +0 -23
  214. package/nitrogen/generated/shared/c++/HybridCactusUtilSpec.hpp +0 -66
  215. package/src/api/Database.ts +0 -188
  216. package/src/api/RemoteLM.ts +0 -273
  217. package/src/config/CactusConfig.ts +0 -11
  218. package/src/native/CactusCrypto.ts +0 -11
  219. package/src/native/CactusDeviceInfo.ts +0 -18
  220. package/src/native/CactusUtil.ts +0 -43
  221. package/src/specs/CactusCrypto.nitro.ts +0 -6
  222. package/src/specs/CactusDeviceInfo.nitro.ts +0 -15
  223. package/src/specs/CactusUtil.nitro.ts +0 -8
  224. package/src/telemetry/Telemetry.ts +0 -236
  225. package/src/types/CactusModel.ts +0 -15
  226. package/src/types/CactusSTTModel.ts +0 -10
@@ -1,273 +0,0 @@
1
- import { CactusConfig } from '../config/CactusConfig';
2
- import { CactusImage } from '../native/CactusImage';
3
- import type {
4
- CactusLMCompleteResult,
5
- Message,
6
- CompleteOptions,
7
- Tool,
8
- } from '../types/CactusLM';
9
-
10
- export class RemoteLM {
11
- private static readonly completionsUrl =
12
- 'https://openrouter.ai/api/v1/chat/completions';
13
-
14
- private static readonly defaultModel = 'google/gemini-2.5-flash-lite';
15
-
16
- public static async complete(
17
- messages: Message[],
18
- options?: CompleteOptions,
19
- tools?: { type: 'function'; function: Tool }[],
20
- callback?: (token: string) => void
21
- ): Promise<CactusLMCompleteResult> {
22
- if (!CactusConfig.cactusToken) {
23
- throw new Error('cactusToken is required for hybrid completions');
24
- }
25
-
26
- const payload = JSON.stringify({
27
- model: this.defaultModel,
28
- messages: await this.transformMessages(messages),
29
- tools,
30
- temperature: options?.temperature,
31
- top_p: options?.topP,
32
- top_k: options?.topK,
33
- max_tokens: options?.maxTokens,
34
- stop: options?.stopSequences,
35
- stream: !!callback,
36
- });
37
-
38
- return callback
39
- ? await this.streamXHR(payload, callback)
40
- : await this.nonStreamFetch(payload);
41
- }
42
-
43
- private static getMimeType(filePath: string): string {
44
- const extension = filePath.toLowerCase().split('.').pop();
45
- switch (extension) {
46
- case 'jpg':
47
- case 'jpeg':
48
- return 'image/jpeg';
49
- case 'png':
50
- return 'image/png';
51
- case 'gif':
52
- return 'image/gif';
53
- case 'webp':
54
- return 'image/webp';
55
- default:
56
- throw new Error(`Unsupported image format: .${extension}`);
57
- }
58
- }
59
-
60
- private static async transformMessages(messages: Message[]) {
61
- const transformedMessages = [];
62
-
63
- for (const message of messages) {
64
- const content: {
65
- type: string;
66
- text?: string;
67
- image_url?: { url: string };
68
- }[] = [];
69
-
70
- if (message.content) {
71
- content.push({
72
- type: 'text',
73
- text: message.content,
74
- });
75
- }
76
-
77
- if (message.images) {
78
- for (const image of message.images) {
79
- const imagePath = image.replace('file://', '');
80
- const mimeType = this.getMimeType(imagePath);
81
- const base64Data = await CactusImage.base64(imagePath);
82
-
83
- content.push({
84
- type: 'image_url',
85
- image_url: {
86
- url: `data:${mimeType};base64,${base64Data}`,
87
- },
88
- });
89
- }
90
- }
91
-
92
- transformedMessages.push({ role: message.role, content });
93
- }
94
-
95
- return transformedMessages;
96
- }
97
-
98
- private static streamXHR(
99
- payload: string,
100
- callback: (token: string) => void
101
- ): Promise<CactusLMCompleteResult> {
102
- return new Promise((resolve, reject) => {
103
- const xhr = new XMLHttpRequest();
104
-
105
- xhr.timeout = 3 * 60 * 1000; // 3 minutes
106
- xhr.ontimeout = () =>
107
- reject(new Error('Remote streaming completion timed out'));
108
-
109
- xhr.open('POST', this.completionsUrl);
110
- xhr.setRequestHeader(
111
- 'Authorization',
112
- `Bearer ${CactusConfig.cactusToken}`
113
- );
114
- xhr.setRequestHeader('HTTP-Referer', 'https://cactuscompute.com');
115
- xhr.setRequestHeader('X-Title', 'Cactus React Native SDK');
116
- xhr.setRequestHeader('Content-Type', 'application/json');
117
-
118
- const startTime = performance.now();
119
- let lastIndex = 0;
120
- let buffer = '';
121
- let response = '';
122
- let toolCalls: { name: string; arguments: string }[] | undefined;
123
- let timeToFirstTokenMs = 0;
124
- let prefillTokens = 0;
125
- let decodeTokens = 0;
126
- let totalTokens = 0;
127
-
128
- xhr.onprogress = () => {
129
- const chunk = xhr.responseText.substring(lastIndex);
130
- lastIndex = xhr.responseText.length;
131
-
132
- buffer += chunk;
133
-
134
- const lines = buffer.split('\n');
135
- buffer = lines.pop() || '';
136
-
137
- for (const line of lines) {
138
- if (!line.startsWith('data: ')) {
139
- continue;
140
- }
141
-
142
- const jsonStr = line.slice(6).trim();
143
- if (jsonStr === '[DONE]') {
144
- continue;
145
- }
146
-
147
- try {
148
- const data = JSON.parse(jsonStr);
149
-
150
- if (timeToFirstTokenMs === 0) {
151
- timeToFirstTokenMs = performance.now() - startTime;
152
- }
153
-
154
- const toolCallChunks = data?.choices?.[0]?.delta?.tool_calls;
155
- if (toolCallChunks) {
156
- if (!toolCalls) {
157
- toolCalls = [];
158
- }
159
-
160
- for (const toolCallChunk of toolCallChunks) {
161
- const index = toolCallChunk.index;
162
-
163
- if (!toolCalls[index]) {
164
- toolCalls[index] = { name: '', arguments: '' };
165
- }
166
-
167
- if (toolCallChunk.function?.name) {
168
- toolCalls[index].name = toolCallChunk.function.name;
169
- }
170
-
171
- if (toolCallChunk.function?.arguments) {
172
- toolCalls[index].arguments +=
173
- toolCallChunk.function.arguments;
174
- }
175
- }
176
- }
177
-
178
- const content = data?.choices?.[0]?.delta?.content;
179
- if (content) {
180
- response += content;
181
- callback(content);
182
- }
183
-
184
- if (data?.usage) {
185
- prefillTokens = data.usage.prompt_tokens;
186
- decodeTokens = data.usage.completion_tokens;
187
- totalTokens = data.usage.total_tokens;
188
- }
189
- } catch {}
190
- }
191
- };
192
-
193
- xhr.onload = () => {
194
- const totalTimeMs = performance.now() - startTime;
195
- const functionCalls = toolCalls?.map((toolCall) => ({
196
- name: toolCall.name,
197
- arguments: JSON.parse(toolCall.arguments) as { [key: string]: any },
198
- }));
199
-
200
- resolve({
201
- success: true,
202
- response,
203
- functionCalls,
204
- timeToFirstTokenMs,
205
- totalTimeMs,
206
- tokensPerSecond: (decodeTokens * 1000) / totalTimeMs,
207
- prefillTokens,
208
- decodeTokens,
209
- totalTokens,
210
- });
211
- };
212
-
213
- xhr.onerror = () =>
214
- reject(new Error('Remote streaming completion failed'));
215
-
216
- xhr.send(payload);
217
- });
218
- }
219
-
220
- private static async nonStreamFetch(
221
- payload: string
222
- ): Promise<CactusLMCompleteResult> {
223
- const startTime = performance.now();
224
-
225
- const request = await fetch(this.completionsUrl, {
226
- method: 'POST',
227
- headers: {
228
- 'Authorization': `Bearer ${CactusConfig.cactusToken}`,
229
- 'HTTP-Referer': 'https://cactuscompute.com',
230
- 'X-Title': 'Cactus React Native SDK',
231
- 'Content-Type': 'application/json',
232
- },
233
- body: payload,
234
- });
235
-
236
- if (!request.ok) {
237
- throw new Error('Remote completion failed');
238
- }
239
-
240
- const data = await request.json();
241
-
242
- const totalTimeMs = performance.now() - startTime;
243
- const decodeTokens = data.usage.completion_tokens;
244
-
245
- const toolCalls:
246
- | {
247
- function: {
248
- name: string;
249
- arguments: {
250
- [key: string]: any;
251
- };
252
- };
253
- }[]
254
- | undefined = data.choices[0].message.tool_calls;
255
-
256
- const functionCalls = toolCalls?.map((toolCall) => ({
257
- name: toolCall.function.name,
258
- arguments: toolCall.function.arguments,
259
- }));
260
-
261
- return {
262
- success: true,
263
- response: data.choices[0].message.content,
264
- functionCalls,
265
- timeToFirstTokenMs: totalTimeMs,
266
- totalTimeMs,
267
- tokensPerSecond: (decodeTokens * 1000) / totalTimeMs,
268
- prefillTokens: data.usage.prompt_tokens,
269
- decodeTokens,
270
- totalTokens: data.usage.total_tokens,
271
- };
272
- }
273
- }
@@ -1,11 +0,0 @@
1
- export class CactusConfig {
2
- // Telemetry
3
- public static telemetryToken?: string;
4
- public static isTelemetryEnabled: boolean = true;
5
-
6
- // Hybrid mode
7
- public static cactusToken?: string;
8
-
9
- // Pro features
10
- public static cactusProKey?: string;
11
- }
@@ -1,11 +0,0 @@
1
- import { NitroModules } from 'react-native-nitro-modules';
2
- import type { CactusCrypto as CactusCryptoSpec } from '../specs/CactusCrypto.nitro';
3
-
4
- export class CactusCrypto {
5
- private static readonly hybridCactusCrypto =
6
- NitroModules.createHybridObject<CactusCryptoSpec>('CactusCrypto');
7
-
8
- public static uuidv5(namespaceUuid: string, name: string): Promise<string> {
9
- return this.hybridCactusCrypto.uuidv5(namespaceUuid, name);
10
- }
11
- }
@@ -1,18 +0,0 @@
1
- import { NitroModules } from 'react-native-nitro-modules';
2
- import type {
3
- CactusDeviceInfo as CactusDeviceInfoSpec,
4
- DeviceInfo,
5
- } from '../specs/CactusDeviceInfo.nitro';
6
-
7
- export class CactusDeviceInfo {
8
- private static readonly hybridCactusDeviceInfo =
9
- NitroModules.createHybridObject<CactusDeviceInfoSpec>('CactusDeviceInfo');
10
-
11
- public static getAppIdentifier(): Promise<string | undefined> {
12
- return this.hybridCactusDeviceInfo.getAppIdentifier();
13
- }
14
-
15
- public static getDeviceInfo(): Promise<DeviceInfo> {
16
- return this.hybridCactusDeviceInfo.getDeviceInfo();
17
- }
18
- }
@@ -1,43 +0,0 @@
1
- import { NitroModules } from 'react-native-nitro-modules';
2
- import type { CactusUtil as CactusUtilSpec } from '../specs/CactusUtil.nitro';
3
- import { Platform } from 'react-native';
4
- import { CactusFileSystem } from './CactusFileSystem';
5
- import { CactusConfig } from '../config/CactusConfig';
6
- import { Database } from '../api/Database';
7
-
8
- export class CactusUtil {
9
- private static readonly hybridCactusUtil =
10
- NitroModules.createHybridObject<CactusUtilSpec>('CactusUtil');
11
-
12
- public static async registerApp(encryptedData: string): Promise<string> {
13
- if (Platform.OS === 'android') {
14
- const cactusDirectory = await CactusFileSystem.getCactusDirectory();
15
- await this.hybridCactusUtil.setAndroidDataDirectory(cactusDirectory);
16
- }
17
-
18
- return this.hybridCactusUtil.registerApp(encryptedData);
19
- }
20
-
21
- public static async getDeviceId(): Promise<string | null> {
22
- if (Platform.OS === 'android') {
23
- const cactusDirectory = await CactusFileSystem.getCactusDirectory();
24
- await this.hybridCactusUtil.setAndroidDataDirectory(cactusDirectory);
25
- }
26
-
27
- const deviceId = await this.hybridCactusUtil.getDeviceId(
28
- CactusConfig.cactusProKey
29
- );
30
-
31
- if (!deviceId) {
32
- return null;
33
- }
34
-
35
- if (deviceId?.indexOf('|') !== -1) {
36
- const parts = deviceId.split('|');
37
- CactusConfig.cactusProKey = parts[1];
38
- return await Database.registerDevice({ deviceId: parts[0] });
39
- }
40
-
41
- return deviceId;
42
- }
43
- }
@@ -1,6 +0,0 @@
1
- import type { HybridObject } from 'react-native-nitro-modules';
2
-
3
- export interface CactusCrypto
4
- extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> {
5
- uuidv5(namespaceUuid: string, name: string): Promise<string>;
6
- }
@@ -1,15 +0,0 @@
1
- import type { HybridObject } from 'react-native-nitro-modules';
2
-
3
- export interface DeviceInfo {
4
- brand: string;
5
- model: string;
6
- device_id?: string;
7
- os: string;
8
- os_version: string;
9
- }
10
-
11
- export interface CactusDeviceInfo
12
- extends HybridObject<{ ios: 'swift'; android: 'kotlin' }> {
13
- getAppIdentifier(): Promise<string | undefined>;
14
- getDeviceInfo(): Promise<DeviceInfo>;
15
- }
@@ -1,8 +0,0 @@
1
- import type { HybridObject } from 'react-native-nitro-modules';
2
-
3
- export interface CactusUtil
4
- extends HybridObject<{ ios: 'c++'; android: 'c++' }> {
5
- registerApp(encryptedData: string): Promise<string>;
6
- getDeviceId(token?: string): Promise<string | null>;
7
- setAndroidDataDirectory(dataDir: string): Promise<void>;
8
- }
@@ -1,236 +0,0 @@
1
- import { Database } from '../api/Database';
2
- import {
3
- CactusCrypto,
4
- CactusDeviceInfo,
5
- CactusFileSystem,
6
- CactusUtil,
7
- } from '../native';
8
- import { CactusConfig } from '../config/CactusConfig';
9
- import { packageVersion } from '../constants/packageVersion';
10
- import type { CactusLMCompleteResult } from '../types/CactusLM';
11
- import type { CactusSTTTranscribeResult } from '../types/CactusSTT';
12
-
13
- export interface LogRecord {
14
- // Framework
15
- framework: 'react-native';
16
- framework_version: string;
17
-
18
- // Event
19
- event_type:
20
- | 'init'
21
- | 'completion'
22
- | 'transcription'
23
- | 'embedding'
24
- | 'image_embedding'
25
- | 'audio_embedding';
26
- model: string;
27
- success: boolean;
28
- message?: string;
29
-
30
- // Telemetry
31
- telemetry_token?: string;
32
- project_id?: string;
33
- device_id?: string;
34
-
35
- // LM
36
- tokens?: number;
37
- response_time?: number;
38
- ttft?: number;
39
- tps?: number;
40
- }
41
-
42
- export class Telemetry {
43
- private static cactusTelemetryToken?: string;
44
- private static projectId?: string;
45
- private static deviceId?: string;
46
-
47
- private static readonly namespaceUrl = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
48
- private static readonly logBufferPaths = {
49
- init: 'logs/init.json',
50
- completion: 'logs/completion.json',
51
- transcription: 'logs/transcription.json',
52
- embedding: 'logs/embedding.json',
53
- image_embedding: 'logs/image_embedding.json',
54
- audio_embedding: 'logs/audio_embedding.json',
55
- };
56
-
57
- private static async handleLog(logRecord: LogRecord) {
58
- if (!this.isInitialized()) {
59
- return;
60
- }
61
-
62
- if (!CactusConfig.isTelemetryEnabled) {
63
- return;
64
- }
65
-
66
- const logBufferPath = this.logBufferPaths[logRecord.event_type];
67
-
68
- let logs = [];
69
- if (await CactusFileSystem.fileExists(logBufferPath)) {
70
- try {
71
- logs = JSON.parse(await CactusFileSystem.readFile(logBufferPath));
72
- } catch {
73
- // Delete corrupted log buffer
74
- await CactusFileSystem.deleteFile(logBufferPath);
75
- }
76
- }
77
- logs.push(logRecord);
78
-
79
- try {
80
- await Database.sendLogRecords(logs);
81
-
82
- if (await CactusFileSystem.fileExists(logBufferPath)) {
83
- await CactusFileSystem.deleteFile(logBufferPath);
84
- }
85
- } catch {
86
- await CactusFileSystem.writeFile(logBufferPath, JSON.stringify(logs));
87
- }
88
- }
89
-
90
- public static isInitialized(): boolean {
91
- return !!(this.projectId && this.deviceId);
92
- }
93
-
94
- public static async init(cactusTelemetryToken?: string): Promise<void> {
95
- if (this.isInitialized()) {
96
- return;
97
- }
98
-
99
- if (!CactusConfig.isTelemetryEnabled) {
100
- return;
101
- }
102
-
103
- this.cactusTelemetryToken = cactusTelemetryToken;
104
-
105
- const appIdentifier = await CactusDeviceInfo.getAppIdentifier();
106
- const name = `https://cactus-react-native/${appIdentifier}/v1`;
107
- this.projectId = await CactusCrypto.uuidv5(this.namespaceUrl, name);
108
-
109
- const deviceInfo = await CactusDeviceInfo.getDeviceInfo();
110
- try {
111
- this.deviceId =
112
- (await CactusUtil.getDeviceId()) ??
113
- (await Database.registerDevice({ deviceData: deviceInfo }));
114
- } catch (error) {
115
- console.log(error);
116
- }
117
- }
118
-
119
- public static logInit(
120
- model: string,
121
- success: boolean,
122
- message?: string
123
- ): Promise<void> {
124
- return this.handleLog({
125
- framework: 'react-native',
126
- framework_version: packageVersion,
127
- event_type: 'init',
128
- model,
129
- success,
130
- message,
131
- telemetry_token: this.cactusTelemetryToken,
132
- project_id: this.projectId,
133
- device_id: this.deviceId,
134
- });
135
- }
136
-
137
- public static logCompletion(
138
- model: string,
139
- success: boolean,
140
- message?: string,
141
- result?: CactusLMCompleteResult
142
- ): Promise<void> {
143
- return this.handleLog({
144
- framework: 'react-native',
145
- framework_version: packageVersion,
146
- event_type: 'completion',
147
- model,
148
- success,
149
- message,
150
- telemetry_token: this.cactusTelemetryToken,
151
- project_id: this.projectId,
152
- device_id: this.deviceId,
153
- tokens: result?.totalTokens,
154
- response_time: result?.totalTimeMs,
155
- ttft: result?.timeToFirstTokenMs,
156
- tps: result?.tokensPerSecond,
157
- });
158
- }
159
-
160
- public static logTranscribe(
161
- model: string,
162
- success: boolean,
163
- message?: string,
164
- result?: CactusSTTTranscribeResult
165
- ): Promise<void> {
166
- return this.handleLog({
167
- framework: 'react-native',
168
- framework_version: packageVersion,
169
- event_type: 'transcription',
170
- model,
171
- success,
172
- message,
173
- telemetry_token: this.cactusTelemetryToken,
174
- project_id: this.projectId,
175
- device_id: this.deviceId,
176
- tokens: result?.totalTokens,
177
- response_time: result?.totalTimeMs,
178
- ttft: result?.timeToFirstTokenMs,
179
- tps: result?.tokensPerSecond,
180
- });
181
- }
182
-
183
- public static logEmbedding(
184
- model: string,
185
- success: boolean,
186
- message?: string
187
- ): Promise<void> {
188
- return this.handleLog({
189
- framework: 'react-native',
190
- framework_version: packageVersion,
191
- event_type: 'embedding',
192
- model,
193
- success,
194
- message,
195
- telemetry_token: this.cactusTelemetryToken,
196
- project_id: this.projectId,
197
- device_id: this.deviceId,
198
- });
199
- }
200
-
201
- public static logImageEmbedding(
202
- model: string,
203
- success: boolean,
204
- message?: string
205
- ): Promise<void> {
206
- return this.handleLog({
207
- framework: 'react-native',
208
- framework_version: packageVersion,
209
- event_type: 'image_embedding',
210
- model,
211
- success,
212
- message,
213
- telemetry_token: this.cactusTelemetryToken,
214
- project_id: this.projectId,
215
- device_id: this.deviceId,
216
- });
217
- }
218
-
219
- public static logAudioEmbedding(
220
- model: string,
221
- success: boolean,
222
- message?: string
223
- ): Promise<void> {
224
- return this.handleLog({
225
- framework: 'react-native',
226
- framework_version: packageVersion,
227
- event_type: 'audio_embedding',
228
- model,
229
- success,
230
- message,
231
- telemetry_token: this.cactusTelemetryToken,
232
- project_id: this.projectId,
233
- device_id: this.deviceId,
234
- });
235
- }
236
- }
@@ -1,15 +0,0 @@
1
- export interface CactusModel {
2
- // API
3
- name: string;
4
- slug: string;
5
- quantization: number;
6
- sizeMb: number;
7
- downloadUrl: string;
8
- supportsToolCalling: boolean;
9
- supportsVision: boolean;
10
- supportsCompletion: boolean;
11
- createdAt: Date;
12
-
13
- // Local
14
- isDownloaded: boolean;
15
- }
@@ -1,10 +0,0 @@
1
- export interface CactusSTTModel {
2
- // API
3
- slug: string;
4
- sizeMb: number;
5
- downloadUrl: string;
6
- createdAt: Date;
7
-
8
- // Local
9
- isDownloaded: boolean;
10
- }