cactus-react-native 1.0.1 → 1.1.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.
- package/README.md +609 -56
- package/android/src/main/java/com/margelo/nitro/cactus/HybridCactusCrypto.kt +23 -15
- package/android/src/main/java/com/margelo/nitro/cactus/HybridCactusDeviceInfo.kt +12 -9
- package/android/src/main/java/com/margelo/nitro/cactus/HybridCactusFileSystem.kt +42 -41
- package/android/src/main/java/com/margelo/nitro/cactus/HybridCactusImage.kt +81 -0
- package/android/src/main/jniLibs/arm64-v8a/libcactus.a +0 -0
- package/cpp/HybridCactus.cpp +161 -44
- package/cpp/HybridCactus.hpp +34 -14
- package/cpp/HybridCactusUtil.cpp +13 -11
- package/cpp/HybridCactusUtil.hpp +9 -9
- package/cpp/cactus_ffi.h +28 -1
- package/ios/HybridCactusImage.swift +53 -0
- package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus_ffi.h +28 -1
- package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/engine.h +237 -7
- package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/ffi_utils.h +158 -43
- package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/graph.h +23 -2
- package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/kernel.h +52 -0
- package/ios/cactus.xcframework/ios-arm64/cactus.framework/cactus +0 -0
- package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus_ffi.h +28 -1
- package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/engine.h +237 -7
- package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/ffi_utils.h +158 -43
- package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/graph.h +23 -2
- package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/kernel.h +52 -0
- package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/cactus +0 -0
- package/lib/module/api/Database.js +23 -0
- package/lib/module/api/Database.js.map +1 -1
- package/lib/module/api/RemoteLM.js +201 -0
- package/lib/module/api/RemoteLM.js.map +1 -0
- package/lib/module/classes/CactusLM.js +56 -28
- package/lib/module/classes/CactusLM.js.map +1 -1
- package/lib/module/classes/CactusSTT.js +137 -0
- package/lib/module/classes/CactusSTT.js.map +1 -0
- package/lib/module/config/CactusConfig.js +4 -0
- package/lib/module/config/CactusConfig.js.map +1 -1
- package/lib/module/constants/packageVersion.js +1 -1
- package/lib/module/hooks/useCactusLM.js +44 -16
- package/lib/module/hooks/useCactusLM.js.map +1 -1
- package/lib/module/hooks/useCactusSTT.js +234 -0
- package/lib/module/hooks/useCactusSTT.js.map +1 -0
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/native/Cactus.js +52 -3
- package/lib/module/native/Cactus.js.map +1 -1
- package/lib/module/native/CactusFileSystem.js +2 -3
- package/lib/module/native/CactusFileSystem.js.map +1 -1
- package/lib/module/native/CactusImage.js +13 -0
- package/lib/module/native/CactusImage.js.map +1 -0
- package/lib/module/native/index.js +1 -0
- package/lib/module/native/index.js.map +1 -1
- package/lib/module/specs/CactusImage.nitro.js +4 -0
- package/lib/module/specs/CactusImage.nitro.js.map +1 -0
- package/lib/module/telemetry/Telemetry.js +53 -1
- package/lib/module/telemetry/Telemetry.js.map +1 -1
- package/lib/module/types/CactusSTT.js +2 -0
- package/lib/module/types/CactusSTT.js.map +1 -0
- package/lib/typescript/src/api/Database.d.ts +1 -0
- package/lib/typescript/src/api/Database.d.ts.map +1 -1
- package/lib/typescript/src/api/RemoteLM.d.ts +14 -0
- package/lib/typescript/src/api/RemoteLM.d.ts.map +1 -0
- package/lib/typescript/src/classes/CactusLM.d.ts +8 -5
- package/lib/typescript/src/classes/CactusLM.d.ts.map +1 -1
- package/lib/typescript/src/classes/CactusSTT.d.ts +25 -0
- package/lib/typescript/src/classes/CactusSTT.d.ts.map +1 -0
- package/lib/typescript/src/config/CactusConfig.d.ts +1 -0
- package/lib/typescript/src/config/CactusConfig.d.ts.map +1 -1
- package/lib/typescript/src/constants/packageVersion.d.ts +1 -1
- package/lib/typescript/src/hooks/useCactusLM.d.ts +5 -4
- package/lib/typescript/src/hooks/useCactusLM.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useCactusSTT.d.ts +20 -0
- package/lib/typescript/src/hooks/useCactusSTT.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native/Cactus.d.ts +10 -3
- package/lib/typescript/src/native/Cactus.d.ts.map +1 -1
- package/lib/typescript/src/native/CactusFileSystem.d.ts +1 -1
- package/lib/typescript/src/native/CactusFileSystem.d.ts.map +1 -1
- package/lib/typescript/src/native/CactusImage.d.ts +6 -0
- package/lib/typescript/src/native/CactusImage.d.ts.map +1 -0
- package/lib/typescript/src/native/index.d.ts +1 -0
- package/lib/typescript/src/native/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/Cactus.nitro.d.ts +4 -1
- package/lib/typescript/src/specs/Cactus.nitro.d.ts.map +1 -1
- package/lib/typescript/src/specs/CactusImage.nitro.d.ts +9 -0
- package/lib/typescript/src/specs/CactusImage.nitro.d.ts.map +1 -0
- package/lib/typescript/src/telemetry/Telemetry.d.ts +5 -1
- package/lib/typescript/src/telemetry/Telemetry.d.ts.map +1 -1
- package/lib/typescript/src/types/CactusLM.d.ts +11 -6
- package/lib/typescript/src/types/CactusLM.d.ts.map +1 -1
- package/lib/typescript/src/types/CactusSTT.d.ts +37 -0
- package/lib/typescript/src/types/CactusSTT.d.ts.map +1 -0
- package/nitro.json +4 -0
- package/nitrogen/generated/android/c++/JHybridCactusImageSpec.cpp +81 -0
- package/nitrogen/generated/android/c++/JHybridCactusImageSpec.hpp +66 -0
- package/nitrogen/generated/android/cactus+autolinking.cmake +2 -0
- package/nitrogen/generated/android/cactusOnLoad.cpp +10 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/cactus/HybridCactusImageSpec.kt +62 -0
- package/nitrogen/generated/ios/Cactus-Swift-Cxx-Bridge.cpp +17 -0
- package/nitrogen/generated/ios/Cactus-Swift-Cxx-Bridge.hpp +17 -0
- package/nitrogen/generated/ios/Cactus-Swift-Cxx-Umbrella.hpp +5 -0
- package/nitrogen/generated/ios/CactusAutolinking.mm +8 -0
- package/nitrogen/generated/ios/CactusAutolinking.swift +15 -0
- package/nitrogen/generated/ios/c++/HybridCactusImageSpecSwift.cpp +11 -0
- package/nitrogen/generated/ios/c++/HybridCactusImageSpecSwift.hpp +85 -0
- package/nitrogen/generated/ios/swift/HybridCactusImageSpec.swift +58 -0
- package/nitrogen/generated/ios/swift/HybridCactusImageSpec_cxx.swift +158 -0
- package/nitrogen/generated/shared/c++/HybridCactusImageSpec.cpp +22 -0
- package/nitrogen/generated/shared/c++/HybridCactusImageSpec.hpp +64 -0
- package/nitrogen/generated/shared/c++/HybridCactusSpec.cpp +3 -0
- package/nitrogen/generated/shared/c++/HybridCactusSpec.hpp +4 -1
- package/package.json +1 -1
- package/src/api/Database.ts +27 -0
- package/src/api/RemoteLM.ts +273 -0
- package/src/classes/CactusLM.ts +76 -40
- package/src/classes/CactusSTT.ts +182 -0
- package/src/config/CactusConfig.ts +4 -0
- package/src/constants/packageVersion.ts +1 -1
- package/src/hooks/useCactusLM.ts +53 -22
- package/src/hooks/useCactusSTT.ts +285 -0
- package/src/index.tsx +14 -2
- package/src/native/Cactus.ts +100 -6
- package/src/native/CactusFileSystem.ts +2 -2
- package/src/native/CactusImage.ts +20 -0
- package/src/native/index.ts +1 -0
- package/src/specs/Cactus.nitro.ts +14 -1
- package/src/specs/CactusImage.nitro.ts +12 -0
- package/src/telemetry/Telemetry.ts +78 -1
- package/src/types/CactusLM.ts +12 -6
- package/src/types/CactusSTT.ts +42 -0
|
@@ -7,23 +7,32 @@ import java.util.Locale
|
|
|
7
7
|
import java.util.UUID
|
|
8
8
|
|
|
9
9
|
class HybridCactusCrypto : HybridCactusCryptoSpec() {
|
|
10
|
-
override fun uuidv5(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
override fun uuidv5(
|
|
11
|
+
namespaceUuid: String,
|
|
12
|
+
name: String,
|
|
13
|
+
): Promise<String> =
|
|
14
|
+
Promise.async {
|
|
15
|
+
val nsUuid =
|
|
16
|
+
try {
|
|
17
|
+
UUID.fromString(namespaceUuid)
|
|
18
|
+
} catch (e: IllegalArgumentException) {
|
|
19
|
+
throw IllegalArgumentException("Invalid namespace UUID")
|
|
20
|
+
}
|
|
17
21
|
|
|
18
|
-
val nsBytes =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
val nsBytes =
|
|
23
|
+
ByteBuffer
|
|
24
|
+
.allocate(16)
|
|
25
|
+
.apply {
|
|
26
|
+
putLong(nsUuid.mostSignificantBits)
|
|
27
|
+
putLong(nsUuid.leastSignificantBits)
|
|
28
|
+
}.array()
|
|
22
29
|
val nameBytes = name.toByteArray(Charsets.UTF_8)
|
|
23
30
|
|
|
24
|
-
val sha1 =
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
val sha1 =
|
|
32
|
+
MessageDigest
|
|
33
|
+
.getInstance("SHA-1")
|
|
34
|
+
.apply { update(nsBytes) }
|
|
35
|
+
.digest(nameBytes)
|
|
27
36
|
val uuidBytes = sha1.copyOfRange(0, 16)
|
|
28
37
|
uuidBytes[6] = (uuidBytes[6].toInt() and 0x0F or 0x50).toByte()
|
|
29
38
|
uuidBytes[8] = (uuidBytes[8].toInt() and 0x3F or 0x80).toByte()
|
|
@@ -34,5 +43,4 @@ class HybridCactusCrypto : HybridCactusCryptoSpec() {
|
|
|
34
43
|
|
|
35
44
|
uuid.toString().lowercase(Locale.ROOT)
|
|
36
45
|
}
|
|
37
|
-
}
|
|
38
46
|
}
|
|
@@ -8,17 +8,20 @@ import com.margelo.nitro.core.Promise
|
|
|
8
8
|
class HybridCactusDeviceInfo : HybridCactusDeviceInfoSpec() {
|
|
9
9
|
private val context = NitroModules.applicationContext ?: error("Android context not found")
|
|
10
10
|
|
|
11
|
-
override fun getAppIdentifier(): Promise<String?> {
|
|
12
|
-
return Promise.async { context.packageName }
|
|
13
|
-
}
|
|
11
|
+
override fun getAppIdentifier(): Promise<String?> = Promise.async { context.packageName }
|
|
14
12
|
|
|
15
|
-
override fun getDeviceInfo(): Promise<DeviceInfo>
|
|
16
|
-
|
|
13
|
+
override fun getDeviceInfo(): Promise<DeviceInfo> =
|
|
14
|
+
Promise.async {
|
|
17
15
|
DeviceInfo(
|
|
18
|
-
brand = Build.MANUFACTURER,
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
brand = Build.MANUFACTURER,
|
|
17
|
+
model = Build.MODEL,
|
|
18
|
+
device_id =
|
|
19
|
+
Settings.Secure.getString(
|
|
20
|
+
context.contentResolver,
|
|
21
|
+
Settings.Secure.ANDROID_ID,
|
|
22
|
+
),
|
|
23
|
+
os = "Android",
|
|
24
|
+
os_version = Build.VERSION.RELEASE,
|
|
21
25
|
)
|
|
22
26
|
}
|
|
23
|
-
}
|
|
24
27
|
}
|
|
@@ -16,29 +16,28 @@ import kotlin.math.floor
|
|
|
16
16
|
class HybridCactusFileSystem : HybridCactusFileSystemSpec() {
|
|
17
17
|
private val context = NitroModules.applicationContext ?: error("Android context not found")
|
|
18
18
|
|
|
19
|
-
override fun getCactusDirectory(): Promise<String> {
|
|
20
|
-
return Promise.async { cactusFile().absolutePath }
|
|
21
|
-
}
|
|
19
|
+
override fun getCactusDirectory(): Promise<String> = Promise.async { cactusFile().absolutePath }
|
|
22
20
|
|
|
23
|
-
override fun fileExists(path: String): Promise<Boolean>
|
|
24
|
-
|
|
21
|
+
override fun fileExists(path: String): Promise<Boolean> =
|
|
22
|
+
Promise.async {
|
|
25
23
|
val cactusDir = cactusFile()
|
|
26
24
|
val file = File(cactusDir, path)
|
|
27
25
|
file.exists()
|
|
28
26
|
}
|
|
29
|
-
}
|
|
30
27
|
|
|
31
|
-
override fun writeFile(
|
|
32
|
-
|
|
28
|
+
override fun writeFile(
|
|
29
|
+
path: String,
|
|
30
|
+
content: String,
|
|
31
|
+
): Promise<Unit> =
|
|
32
|
+
Promise.async {
|
|
33
33
|
val cactusDir = cactusFile()
|
|
34
34
|
val file = File(cactusDir, path)
|
|
35
35
|
file.parentFile?.mkdirs()
|
|
36
36
|
file.writeText(content)
|
|
37
37
|
}
|
|
38
|
-
}
|
|
39
38
|
|
|
40
|
-
override fun readFile(path: String): Promise<String>
|
|
41
|
-
|
|
39
|
+
override fun readFile(path: String): Promise<String> =
|
|
40
|
+
Promise.async {
|
|
42
41
|
val cactusDir = cactusFile()
|
|
43
42
|
val file = File(cactusDir, path)
|
|
44
43
|
|
|
@@ -48,10 +47,9 @@ class HybridCactusFileSystem : HybridCactusFileSystemSpec() {
|
|
|
48
47
|
|
|
49
48
|
file.readText()
|
|
50
49
|
}
|
|
51
|
-
}
|
|
52
50
|
|
|
53
|
-
override fun deleteFile(path: String): Promise<Unit>
|
|
54
|
-
|
|
51
|
+
override fun deleteFile(path: String): Promise<Unit> =
|
|
52
|
+
Promise.async {
|
|
55
53
|
val cactusDir = cactusFile()
|
|
56
54
|
val file = File(cactusDir, path)
|
|
57
55
|
|
|
@@ -61,18 +59,15 @@ class HybridCactusFileSystem : HybridCactusFileSystemSpec() {
|
|
|
61
59
|
|
|
62
60
|
file.deleteRecursively()
|
|
63
61
|
}
|
|
64
|
-
}
|
|
65
62
|
|
|
66
|
-
override fun modelExists(model: String): Promise<Boolean> {
|
|
67
|
-
return Promise.async { modelFile(model).exists() }
|
|
68
|
-
}
|
|
63
|
+
override fun modelExists(model: String): Promise<Boolean> = Promise.async { modelFile(model).exists() }
|
|
69
64
|
|
|
70
|
-
override fun getModelPath(model: String): Promise<String> {
|
|
71
|
-
return Promise.async { modelFile(model).absolutePath }
|
|
72
|
-
}
|
|
65
|
+
override fun getModelPath(model: String): Promise<String> = Promise.async { modelFile(model).absolutePath }
|
|
73
66
|
|
|
74
67
|
override fun downloadModel(
|
|
75
|
-
model: String,
|
|
68
|
+
model: String,
|
|
69
|
+
from: String,
|
|
70
|
+
callback: ((progress: Double) -> Unit)?,
|
|
76
71
|
): Promise<Unit> {
|
|
77
72
|
return Promise.async {
|
|
78
73
|
val modelFile = modelFile(model)
|
|
@@ -82,21 +77,23 @@ class HybridCactusFileSystem : HybridCactusFileSystemSpec() {
|
|
|
82
77
|
return@async
|
|
83
78
|
}
|
|
84
79
|
|
|
85
|
-
val url =
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
80
|
+
val url =
|
|
81
|
+
try {
|
|
82
|
+
URL(from)
|
|
83
|
+
} catch (_: Throwable) {
|
|
84
|
+
throw Error("Invalid URL")
|
|
85
|
+
}
|
|
90
86
|
|
|
91
87
|
val tmpZip = File.createTempFile("dl_", ".zip", context.cacheDir)
|
|
92
88
|
var connection: HttpURLConnection? = null
|
|
93
89
|
|
|
94
90
|
try {
|
|
95
|
-
connection =
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
91
|
+
connection =
|
|
92
|
+
(url.openConnection() as HttpURLConnection).apply {
|
|
93
|
+
connectTimeout = 30_000
|
|
94
|
+
readTimeout = 5 * 60_000
|
|
95
|
+
instanceFollowRedirects = true
|
|
96
|
+
}
|
|
100
97
|
connection.connect()
|
|
101
98
|
val code = connection.responseCode
|
|
102
99
|
|
|
@@ -129,11 +126,13 @@ class HybridCactusFileSystem : HybridCactusFileSystemSpec() {
|
|
|
129
126
|
|
|
130
127
|
if (contentLength > 0) {
|
|
131
128
|
// cap at 0.99; 1.0 will be sent after unzip
|
|
132
|
-
val pct =
|
|
133
|
-
(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
129
|
+
val pct =
|
|
130
|
+
floor(
|
|
131
|
+
(downloaded.toDouble() / contentLength.toDouble()).coerceIn(
|
|
132
|
+
0.0,
|
|
133
|
+
1.0,
|
|
134
|
+
) * 99,
|
|
135
|
+
) / 100.0
|
|
137
136
|
|
|
138
137
|
if (pct - lastPct >= 0.01) {
|
|
139
138
|
callback?.invoke(pct)
|
|
@@ -160,7 +159,10 @@ class HybridCactusFileSystem : HybridCactusFileSystemSpec() {
|
|
|
160
159
|
}
|
|
161
160
|
}
|
|
162
161
|
|
|
163
|
-
private fun unzipItem(
|
|
162
|
+
private fun unzipItem(
|
|
163
|
+
zipFile: File,
|
|
164
|
+
outDir: File,
|
|
165
|
+
) {
|
|
164
166
|
val outRoot = outDir.canonicalFile
|
|
165
167
|
ZipInputStream(BufferedInputStream(FileInputStream(zipFile))).use { zis ->
|
|
166
168
|
var entry: ZipEntry? = zis.nextEntry
|
|
@@ -196,8 +198,8 @@ class HybridCactusFileSystem : HybridCactusFileSystemSpec() {
|
|
|
196
198
|
}
|
|
197
199
|
}
|
|
198
200
|
|
|
199
|
-
override fun deleteModel(model: String): Promise<Unit>
|
|
200
|
-
|
|
201
|
+
override fun deleteModel(model: String): Promise<Unit> =
|
|
202
|
+
Promise.async {
|
|
201
203
|
val modelFile = modelFile(model)
|
|
202
204
|
|
|
203
205
|
if (!modelFile.exists()) {
|
|
@@ -206,7 +208,6 @@ class HybridCactusFileSystem : HybridCactusFileSystemSpec() {
|
|
|
206
208
|
|
|
207
209
|
modelFile.deleteRecursively()
|
|
208
210
|
}
|
|
209
|
-
}
|
|
210
211
|
|
|
211
212
|
private fun cactusFile(): File {
|
|
212
213
|
val documentsDir =
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
package com.margelo.nitro.cactus
|
|
2
|
+
|
|
3
|
+
import android.graphics.Bitmap
|
|
4
|
+
import android.graphics.BitmapFactory
|
|
5
|
+
import androidx.core.graphics.createBitmap
|
|
6
|
+
import com.margelo.nitro.NitroModules
|
|
7
|
+
import com.margelo.nitro.core.Promise
|
|
8
|
+
import java.io.File
|
|
9
|
+
import java.io.FileOutputStream
|
|
10
|
+
import java.util.UUID
|
|
11
|
+
|
|
12
|
+
class HybridCactusImage : HybridCactusImageSpec() {
|
|
13
|
+
private val context = NitroModules.applicationContext ?: error("Android context not found")
|
|
14
|
+
|
|
15
|
+
override fun base64(path: String): Promise<String> =
|
|
16
|
+
Promise.async {
|
|
17
|
+
val file = File(path)
|
|
18
|
+
|
|
19
|
+
if (!file.exists()) {
|
|
20
|
+
throw Error("No such file: $path")
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
val imageData = file.readBytes()
|
|
24
|
+
android.util.Base64.encodeToString(imageData, android.util.Base64.NO_WRAP)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
override fun resize(
|
|
28
|
+
path: String,
|
|
29
|
+
height: Double,
|
|
30
|
+
width: Double,
|
|
31
|
+
quality: Double,
|
|
32
|
+
): Promise<String> =
|
|
33
|
+
Promise.async {
|
|
34
|
+
val file = File(path)
|
|
35
|
+
|
|
36
|
+
if (!file.exists()) {
|
|
37
|
+
throw Error("No such file: $path")
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
val bitmap = BitmapFactory.decodeFile(path)
|
|
41
|
+
if (bitmap == null) {
|
|
42
|
+
throw Error("Failed to load image from: $path")
|
|
43
|
+
}
|
|
44
|
+
val resizedBitmap = createBitmap(width.toInt(), height.toInt(), Bitmap.Config.RGB_565)
|
|
45
|
+
val canvas = android.graphics.Canvas(resizedBitmap)
|
|
46
|
+
val paint =
|
|
47
|
+
android.graphics.Paint().apply {
|
|
48
|
+
isFilterBitmap = true
|
|
49
|
+
isAntiAlias = true
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
val scaleX = width.toFloat() / bitmap.width
|
|
53
|
+
val scaleY = height.toFloat() / bitmap.height
|
|
54
|
+
val matrix =
|
|
55
|
+
android.graphics.Matrix().apply {
|
|
56
|
+
setScale(scaleX, scaleY)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
canvas.drawBitmap(bitmap, matrix, paint)
|
|
60
|
+
bitmap.recycle()
|
|
61
|
+
|
|
62
|
+
val cacheDir = context.cacheDir
|
|
63
|
+
val outputDir = File(cacheDir, "cactus/images")
|
|
64
|
+
outputDir.mkdirs()
|
|
65
|
+
|
|
66
|
+
val fileName = "${UUID.randomUUID()}.jpg"
|
|
67
|
+
val outputFile = File(outputDir, fileName)
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
FileOutputStream(outputFile).use { outputStream ->
|
|
71
|
+
val qualityInt = (quality * 100).toInt().coerceIn(0, 100)
|
|
72
|
+
resizedBitmap.compress(Bitmap.CompressFormat.JPEG, qualityInt, outputStream)
|
|
73
|
+
}
|
|
74
|
+
resizedBitmap.recycle()
|
|
75
|
+
outputFile.absolutePath
|
|
76
|
+
} catch (e: Exception) {
|
|
77
|
+
resizedBitmap.recycle()
|
|
78
|
+
throw Error("Failed to save resized image: ${e.message}")
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
Binary file
|
package/cpp/HybridCactus.cpp
CHANGED
|
@@ -1,30 +1,41 @@
|
|
|
1
1
|
#include "HybridCactus.hpp"
|
|
2
2
|
|
|
3
|
-
namespace margelo::nitro::cactus
|
|
4
|
-
{
|
|
3
|
+
namespace margelo::nitro::cactus {
|
|
5
4
|
HybridCactus::HybridCactus() : HybridObject(TAG) {}
|
|
6
5
|
|
|
7
|
-
std::shared_ptr<Promise<void>>
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
std::shared_ptr<Promise<void>>
|
|
7
|
+
HybridCactus::init(const std::string &modelPath, double contextSize,
|
|
8
|
+
const std::optional<std::string> &corpusDir) {
|
|
9
|
+
return Promise<void>::async(
|
|
10
|
+
[this, modelPath, contextSize, corpusDir]() -> void {
|
|
11
|
+
std::lock_guard<std::mutex> lock(this->_modelMutex);
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
if (this->_model) {
|
|
14
|
+
throw std::runtime_error("Cactus model is already initialized");
|
|
15
|
+
}
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
const cactus_model_t model =
|
|
18
|
+
cactus_init(modelPath.c_str(), contextSize,
|
|
19
|
+
corpusDir ? corpusDir->c_str() : nullptr);
|
|
16
20
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
if (!model) {
|
|
22
|
+
throw std::runtime_error("Failed to initialize Cactus model");
|
|
23
|
+
}
|
|
20
24
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
this->_model = model;
|
|
26
|
+
this->_contextSize = contextSize;
|
|
27
|
+
});
|
|
24
28
|
}
|
|
25
29
|
|
|
26
|
-
std::shared_ptr<Promise<std::string>> HybridCactus::complete(
|
|
27
|
-
|
|
30
|
+
std::shared_ptr<Promise<std::string>> HybridCactus::complete(
|
|
31
|
+
const std::string &messagesJson, double responseBufferSize,
|
|
32
|
+
const std::optional<std::string> &optionsJson,
|
|
33
|
+
const std::optional<std::string> &toolsJson,
|
|
34
|
+
const std::optional<std::function<void(const std::string & /* token */,
|
|
35
|
+
double /* tokenId */)>> &callback) {
|
|
36
|
+
return Promise<std::string>::async([this, messagesJson, optionsJson,
|
|
37
|
+
toolsJson, callback,
|
|
38
|
+
responseBufferSize]() -> std::string {
|
|
28
39
|
std::lock_guard<std::mutex> lock(this->_modelMutex);
|
|
29
40
|
|
|
30
41
|
if (!this->_model) {
|
|
@@ -32,28 +43,26 @@ std::shared_ptr<Promise<std::string>> HybridCactus::complete(const std::string &
|
|
|
32
43
|
}
|
|
33
44
|
|
|
34
45
|
struct CallbackCtx {
|
|
35
|
-
const std::function<void(const std::string & /* token */,
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
const std::function<void(const std::string & /* token */,
|
|
47
|
+
double /* tokenId */)> *callback;
|
|
48
|
+
} callbackCtx{callback.has_value() ? &callback.value() : nullptr};
|
|
49
|
+
|
|
50
|
+
auto cactusTokenCallback = [](const char *token, uint32_t tokenId,
|
|
51
|
+
void *userData) {
|
|
52
|
+
auto *callbackCtx = static_cast<CallbackCtx *>(userData);
|
|
53
|
+
if (!callbackCtx || !callbackCtx->callback || !(*callbackCtx->callback))
|
|
54
|
+
return;
|
|
41
55
|
(*callbackCtx->callback)(token, tokenId);
|
|
42
56
|
};
|
|
43
57
|
|
|
44
58
|
std::string responseBuffer;
|
|
45
59
|
responseBuffer.resize(responseBufferSize);
|
|
46
60
|
|
|
47
|
-
int result = cactus_complete(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
optionsJson ? optionsJson->c_str() : nullptr,
|
|
53
|
-
toolsJson ? toolsJson->c_str() : nullptr,
|
|
54
|
-
cactusTokenCallback,
|
|
55
|
-
&callbackCtx
|
|
56
|
-
);
|
|
61
|
+
int result = cactus_complete(this->_model, messagesJson.c_str(),
|
|
62
|
+
responseBuffer.data(), responseBufferSize,
|
|
63
|
+
optionsJson ? optionsJson->c_str() : nullptr,
|
|
64
|
+
toolsJson ? toolsJson->c_str() : nullptr,
|
|
65
|
+
cactusTokenCallback, &callbackCtx);
|
|
57
66
|
|
|
58
67
|
if (result < 0) {
|
|
59
68
|
throw std::runtime_error("Cactus completion failed");
|
|
@@ -66,29 +75,139 @@ std::shared_ptr<Promise<std::string>> HybridCactus::complete(const std::string &
|
|
|
66
75
|
});
|
|
67
76
|
}
|
|
68
77
|
|
|
69
|
-
std::shared_ptr<Promise<std::
|
|
70
|
-
|
|
78
|
+
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,
|
|
81
|
+
const std::optional<std::function<void(const std::string & /* token */,
|
|
82
|
+
double /* tokenId */)>> &callback) {
|
|
83
|
+
return Promise<std::string>::async([this, audioFilePath, prompt, optionsJson,
|
|
84
|
+
callback,
|
|
85
|
+
responseBufferSize]() -> std::string {
|
|
71
86
|
std::lock_guard<std::mutex> lock(this->_modelMutex);
|
|
72
87
|
|
|
73
88
|
if (!this->_model) {
|
|
74
89
|
throw std::runtime_error("Cactus model is not initialized");
|
|
75
90
|
}
|
|
76
91
|
|
|
77
|
-
|
|
78
|
-
|
|
92
|
+
struct CallbackCtx {
|
|
93
|
+
const std::function<void(const std::string & /* token */,
|
|
94
|
+
double /* tokenId */)> *callback;
|
|
95
|
+
} callbackCtx{callback.has_value() ? &callback.value() : nullptr};
|
|
96
|
+
|
|
97
|
+
auto cactusTokenCallback = [](const char *token, uint32_t tokenId,
|
|
98
|
+
void *userData) {
|
|
99
|
+
auto *callbackCtx = static_cast<CallbackCtx *>(userData);
|
|
100
|
+
if (!callbackCtx || !callbackCtx->callback || !(*callbackCtx->callback))
|
|
101
|
+
return;
|
|
102
|
+
(*callbackCtx->callback)(token, tokenId);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
std::string responseBuffer;
|
|
106
|
+
responseBuffer.resize(responseBufferSize);
|
|
79
107
|
|
|
80
|
-
int result =
|
|
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);
|
|
81
113
|
|
|
82
114
|
if (result < 0) {
|
|
83
|
-
throw std::runtime_error("Cactus
|
|
115
|
+
throw std::runtime_error("Cactus transcription failed");
|
|
84
116
|
}
|
|
85
117
|
|
|
86
|
-
|
|
118
|
+
// Remove null terminator
|
|
119
|
+
responseBuffer.resize(strlen(responseBuffer.c_str()));
|
|
87
120
|
|
|
88
|
-
return
|
|
121
|
+
return responseBuffer;
|
|
89
122
|
});
|
|
90
123
|
}
|
|
91
124
|
|
|
125
|
+
std::shared_ptr<Promise<std::vector<double>>>
|
|
126
|
+
HybridCactus::embed(const std::string &text, double embeddingBufferSize) {
|
|
127
|
+
return Promise<std::vector<double>>::async(
|
|
128
|
+
[this, text, embeddingBufferSize]() -> std::vector<double> {
|
|
129
|
+
std::lock_guard<std::mutex> lock(this->_modelMutex);
|
|
130
|
+
|
|
131
|
+
if (!this->_model) {
|
|
132
|
+
throw std::runtime_error("Cactus model is not initialized");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
std::vector<float> embeddingBuffer(embeddingBufferSize);
|
|
136
|
+
size_t embeddingDim;
|
|
137
|
+
|
|
138
|
+
int result =
|
|
139
|
+
cactus_embed(this->_model, text.c_str(), embeddingBuffer.data(),
|
|
140
|
+
embeddingBufferSize * sizeof(float), &embeddingDim);
|
|
141
|
+
|
|
142
|
+
if (result < 0) {
|
|
143
|
+
throw std::runtime_error("Cactus embedding failed");
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
embeddingBuffer.resize(embeddingDim);
|
|
147
|
+
|
|
148
|
+
return std::vector<double>(embeddingBuffer.begin(),
|
|
149
|
+
embeddingBuffer.end());
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
std::shared_ptr<Promise<std::vector<double>>>
|
|
154
|
+
HybridCactus::imageEmbed(const std::string &imagePath,
|
|
155
|
+
double embeddingBufferSize) {
|
|
156
|
+
return Promise<std::vector<double>>::async(
|
|
157
|
+
[this, imagePath, embeddingBufferSize]() -> std::vector<double> {
|
|
158
|
+
std::lock_guard<std::mutex> lock(this->_modelMutex);
|
|
159
|
+
|
|
160
|
+
if (!this->_model) {
|
|
161
|
+
throw std::runtime_error("Cactus model is not initialized");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
std::vector<float> embeddingBuffer(embeddingBufferSize);
|
|
165
|
+
size_t embeddingDim;
|
|
166
|
+
|
|
167
|
+
int result = cactus_image_embed(
|
|
168
|
+
this->_model, imagePath.c_str(), embeddingBuffer.data(),
|
|
169
|
+
embeddingBufferSize * sizeof(float), &embeddingDim);
|
|
170
|
+
|
|
171
|
+
if (result < 0) {
|
|
172
|
+
throw std::runtime_error("Cactus image embedding failed");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
embeddingBuffer.resize(embeddingDim);
|
|
176
|
+
|
|
177
|
+
return std::vector<double>(embeddingBuffer.begin(),
|
|
178
|
+
embeddingBuffer.end());
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
std::shared_ptr<Promise<std::vector<double>>>
|
|
183
|
+
HybridCactus::audioEmbed(const std::string &audioPath,
|
|
184
|
+
double embeddingBufferSize) {
|
|
185
|
+
return Promise<std::vector<double>>::async(
|
|
186
|
+
[this, audioPath, embeddingBufferSize]() -> std::vector<double> {
|
|
187
|
+
std::lock_guard<std::mutex> lock(this->_modelMutex);
|
|
188
|
+
|
|
189
|
+
if (!this->_model) {
|
|
190
|
+
throw std::runtime_error("Cactus model is not initialized");
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
std::vector<float> embeddingBuffer(embeddingBufferSize);
|
|
194
|
+
size_t embeddingDim;
|
|
195
|
+
|
|
196
|
+
int result = cactus_audio_embed(
|
|
197
|
+
this->_model, audioPath.c_str(), embeddingBuffer.data(),
|
|
198
|
+
embeddingBufferSize * sizeof(float), &embeddingDim);
|
|
199
|
+
|
|
200
|
+
if (result < 0) {
|
|
201
|
+
throw std::runtime_error("Cactus audio embedding failed");
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
embeddingBuffer.resize(embeddingDim);
|
|
205
|
+
|
|
206
|
+
return std::vector<double>(embeddingBuffer.begin(),
|
|
207
|
+
embeddingBuffer.end());
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
92
211
|
std::shared_ptr<Promise<void>> HybridCactus::reset() {
|
|
93
212
|
return Promise<void>::async([this]() -> void {
|
|
94
213
|
std::lock_guard<std::mutex> lock(this->_modelMutex);
|
|
@@ -102,9 +221,7 @@ std::shared_ptr<Promise<void>> HybridCactus::reset() {
|
|
|
102
221
|
}
|
|
103
222
|
|
|
104
223
|
std::shared_ptr<Promise<void>> HybridCactus::stop() {
|
|
105
|
-
return Promise<void>::async([this]() -> void {
|
|
106
|
-
cactus_stop(this->_model);
|
|
107
|
-
});
|
|
224
|
+
return Promise<void>::async([this]() -> void { cactus_stop(this->_model); });
|
|
108
225
|
}
|
|
109
226
|
|
|
110
227
|
std::shared_ptr<Promise<void>> HybridCactus::destroy() {
|
package/cpp/HybridCactus.hpp
CHANGED
|
@@ -5,26 +5,46 @@
|
|
|
5
5
|
|
|
6
6
|
#include <mutex>
|
|
7
7
|
|
|
8
|
-
namespace margelo::nitro::cactus
|
|
9
|
-
{
|
|
8
|
+
namespace margelo::nitro::cactus {
|
|
10
9
|
|
|
11
|
-
class HybridCactus : public HybridCactusSpec
|
|
12
|
-
{
|
|
10
|
+
class HybridCactus : public HybridCactusSpec {
|
|
13
11
|
public:
|
|
14
12
|
HybridCactus();
|
|
15
|
-
|
|
16
|
-
std::shared_ptr<Promise<void>>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
std::shared_ptr<Promise<std::
|
|
21
|
-
|
|
13
|
+
|
|
14
|
+
std::shared_ptr<Promise<void>>
|
|
15
|
+
init(const std::string &modelPath, double contextSize,
|
|
16
|
+
const std::optional<std::string> &corpusDir) override;
|
|
17
|
+
|
|
18
|
+
std::shared_ptr<Promise<std::string>> complete(
|
|
19
|
+
const std::string &messagesJson, double responseBufferSize,
|
|
20
|
+
const std::optional<std::string> &optionsJson,
|
|
21
|
+
const std::optional<std::string> &toolsJson,
|
|
22
|
+
const std::optional<std::function<void(const std::string & /* token */,
|
|
23
|
+
double /* tokenId */)>> &callback)
|
|
24
|
+
override;
|
|
25
|
+
|
|
26
|
+
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,
|
|
29
|
+
const std::optional<std::function<void(const std::string & /* token */,
|
|
30
|
+
double /* tokenId */)>> &callback)
|
|
31
|
+
override;
|
|
32
|
+
|
|
33
|
+
std::shared_ptr<Promise<std::vector<double>>>
|
|
34
|
+
embed(const std::string &text, double embeddingBufferSize) override;
|
|
35
|
+
|
|
36
|
+
std::shared_ptr<Promise<std::vector<double>>>
|
|
37
|
+
imageEmbed(const std::string &imagePath, double embeddingBufferSize) override;
|
|
38
|
+
|
|
39
|
+
std::shared_ptr<Promise<std::vector<double>>>
|
|
40
|
+
audioEmbed(const std::string &audioPath, double embeddingBufferSize) override;
|
|
41
|
+
|
|
22
42
|
std::shared_ptr<Promise<void>> reset() override;
|
|
23
|
-
|
|
43
|
+
|
|
24
44
|
std::shared_ptr<Promise<void>> stop() override;
|
|
25
|
-
|
|
45
|
+
|
|
26
46
|
std::shared_ptr<Promise<void>> destroy() override;
|
|
27
|
-
|
|
47
|
+
|
|
28
48
|
private:
|
|
29
49
|
cactus_model_t _model = nullptr;
|
|
30
50
|
size_t _contextSize;
|