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
package/src/classes/CactusLM.ts
CHANGED
|
@@ -5,7 +5,8 @@ import type {
|
|
|
5
5
|
CactusLMCompleteResult,
|
|
6
6
|
CactusLMEmbedParams,
|
|
7
7
|
CactusLMEmbedResult,
|
|
8
|
-
|
|
8
|
+
CactusLMImageEmbedParams,
|
|
9
|
+
CactusLMImageEmbedResult,
|
|
9
10
|
CactusLMParams,
|
|
10
11
|
} from '../types/CactusLM';
|
|
11
12
|
import type { CactusModel } from '../types/CactusModel';
|
|
@@ -13,12 +14,14 @@ import { Telemetry } from '../telemetry/Telemetry';
|
|
|
13
14
|
import { CactusConfig } from '../config/CactusConfig';
|
|
14
15
|
import { Database } from '../api/Database';
|
|
15
16
|
import { getErrorMessage } from '../utils/error';
|
|
17
|
+
import { RemoteLM } from '../api/RemoteLM';
|
|
16
18
|
|
|
17
19
|
export class CactusLM {
|
|
18
20
|
private readonly cactus = new Cactus();
|
|
19
21
|
|
|
20
22
|
private readonly model: string;
|
|
21
23
|
private readonly contextSize: number;
|
|
24
|
+
private readonly corpusDir?: string;
|
|
22
25
|
|
|
23
26
|
private isDownloading = false;
|
|
24
27
|
private isInitialized = false;
|
|
@@ -29,13 +32,17 @@ export class CactusLM {
|
|
|
29
32
|
private static readonly defaultCompleteOptions = {
|
|
30
33
|
maxTokens: 512,
|
|
31
34
|
};
|
|
35
|
+
private static readonly defaultCompleteMode = 'local';
|
|
32
36
|
private static readonly defaultEmbedBufferSize = 2048;
|
|
33
37
|
|
|
34
|
-
private static
|
|
38
|
+
private static cactusModelsCache: CactusModel[] | null = null;
|
|
39
|
+
|
|
40
|
+
constructor({ model, contextSize, corpusDir }: CactusLMParams = {}) {
|
|
41
|
+
Telemetry.init(CactusConfig.telemetryToken);
|
|
35
42
|
|
|
36
|
-
constructor({ model, contextSize }: CactusLMParams = {}) {
|
|
37
43
|
this.model = model ?? CactusLM.defaultModel;
|
|
38
44
|
this.contextSize = contextSize ?? CactusLM.defaultContextSize;
|
|
45
|
+
this.corpusDir = corpusDir;
|
|
39
46
|
}
|
|
40
47
|
|
|
41
48
|
public async download({
|
|
@@ -52,8 +59,12 @@ export class CactusLM {
|
|
|
52
59
|
|
|
53
60
|
this.isDownloading = true;
|
|
54
61
|
try {
|
|
55
|
-
await
|
|
56
|
-
await
|
|
62
|
+
const model = await Database.getModel(this.model);
|
|
63
|
+
await CactusFileSystem.downloadModel(
|
|
64
|
+
this.model,
|
|
65
|
+
model.downloadUrl,
|
|
66
|
+
onProgress
|
|
67
|
+
);
|
|
57
68
|
} finally {
|
|
58
69
|
this.isDownloading = false;
|
|
59
70
|
}
|
|
@@ -64,10 +75,6 @@ export class CactusLM {
|
|
|
64
75
|
return;
|
|
65
76
|
}
|
|
66
77
|
|
|
67
|
-
if (!Telemetry.isInitialized()) {
|
|
68
|
-
await Telemetry.init(CactusConfig.telemetryToken);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
78
|
if (!(await CactusFileSystem.modelExists(this.model))) {
|
|
72
79
|
throw new Error(`Model "${this.model}" is not downloaded`);
|
|
73
80
|
}
|
|
@@ -75,7 +82,7 @@ export class CactusLM {
|
|
|
75
82
|
const modelPath = await CactusFileSystem.getModelPath(this.model);
|
|
76
83
|
|
|
77
84
|
try {
|
|
78
|
-
await this.cactus.init(modelPath, this.contextSize);
|
|
85
|
+
await this.cactus.init(modelPath, this.contextSize, this.corpusDir);
|
|
79
86
|
Telemetry.logInit(this.model, true);
|
|
80
87
|
this.isInitialized = true;
|
|
81
88
|
} catch (error) {
|
|
@@ -89,25 +96,32 @@ export class CactusLM {
|
|
|
89
96
|
options,
|
|
90
97
|
tools,
|
|
91
98
|
onToken,
|
|
99
|
+
mode,
|
|
92
100
|
}: CactusLMCompleteParams): Promise<CactusLMCompleteResult> {
|
|
93
101
|
if (this.isGenerating) {
|
|
94
102
|
throw new Error('CactusLM is already generating');
|
|
95
103
|
}
|
|
96
104
|
|
|
97
|
-
await this.init();
|
|
98
|
-
|
|
99
105
|
options = { ...CactusLM.defaultCompleteOptions, ...options };
|
|
106
|
+
const toolsInternal = tools?.map((tool) => ({
|
|
107
|
+
type: 'function' as const,
|
|
108
|
+
function: tool,
|
|
109
|
+
}));
|
|
110
|
+
mode = mode ?? CactusLM.defaultCompleteMode;
|
|
111
|
+
|
|
100
112
|
const responseBufferSize =
|
|
101
113
|
8 * (options.maxTokens ?? CactusLM.defaultCompleteOptions.maxTokens) +
|
|
102
114
|
256;
|
|
103
115
|
|
|
104
|
-
this.isGenerating = true;
|
|
105
116
|
try {
|
|
117
|
+
await this.init();
|
|
118
|
+
|
|
119
|
+
this.isGenerating = true;
|
|
106
120
|
const result = await this.cactus.complete(
|
|
107
121
|
messages,
|
|
108
122
|
responseBufferSize,
|
|
109
123
|
options,
|
|
110
|
-
|
|
124
|
+
toolsInternal,
|
|
111
125
|
onToken
|
|
112
126
|
);
|
|
113
127
|
Telemetry.logCompletion(
|
|
@@ -117,9 +131,25 @@ export class CactusLM {
|
|
|
117
131
|
result
|
|
118
132
|
);
|
|
119
133
|
return result;
|
|
120
|
-
} catch (
|
|
121
|
-
|
|
122
|
-
|
|
134
|
+
} catch (localError) {
|
|
135
|
+
if (mode === 'local') {
|
|
136
|
+
Telemetry.logCompletion(this.model, false, getErrorMessage(localError));
|
|
137
|
+
throw localError;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
Telemetry.logCompletion(
|
|
141
|
+
this.model,
|
|
142
|
+
false,
|
|
143
|
+
`Local completion error: ${getErrorMessage(localError)}. Falling back to remote completion.`
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
return RemoteLM.complete(messages, options, toolsInternal, onToken);
|
|
148
|
+
} catch (remoteError) {
|
|
149
|
+
throw new Error(
|
|
150
|
+
`Remote completion error: ${getErrorMessage(remoteError)}`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
123
153
|
} finally {
|
|
124
154
|
this.isGenerating = false;
|
|
125
155
|
}
|
|
@@ -150,6 +180,31 @@ export class CactusLM {
|
|
|
150
180
|
}
|
|
151
181
|
}
|
|
152
182
|
|
|
183
|
+
public async imageEmbed({
|
|
184
|
+
imagePath,
|
|
185
|
+
}: CactusLMImageEmbedParams): Promise<CactusLMImageEmbedResult> {
|
|
186
|
+
if (this.isGenerating) {
|
|
187
|
+
throw new Error('CactusLM is already generating');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
await this.init();
|
|
191
|
+
|
|
192
|
+
this.isGenerating = true;
|
|
193
|
+
try {
|
|
194
|
+
const embedding = await this.cactus.imageEmbed(
|
|
195
|
+
imagePath,
|
|
196
|
+
CactusLM.defaultEmbedBufferSize
|
|
197
|
+
);
|
|
198
|
+
Telemetry.logImageEmbedding(this.model, true);
|
|
199
|
+
return { embedding };
|
|
200
|
+
} catch (error) {
|
|
201
|
+
Telemetry.logImageEmbedding(this.model, false, getErrorMessage(error));
|
|
202
|
+
throw error;
|
|
203
|
+
} finally {
|
|
204
|
+
this.isGenerating = false;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
153
208
|
public stop(): Promise<void> {
|
|
154
209
|
return this.cactus.stop();
|
|
155
210
|
}
|
|
@@ -170,34 +225,15 @@ export class CactusLM {
|
|
|
170
225
|
this.isInitialized = false;
|
|
171
226
|
}
|
|
172
227
|
|
|
173
|
-
public async getModels({
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (
|
|
177
|
-
!forceRefresh &&
|
|
178
|
-
(await CactusFileSystem.fileExists(CactusLM.modelsInfoPath))
|
|
179
|
-
) {
|
|
180
|
-
try {
|
|
181
|
-
return JSON.parse(
|
|
182
|
-
await CactusFileSystem.readFile(CactusLM.modelsInfoPath)
|
|
183
|
-
);
|
|
184
|
-
} catch {
|
|
185
|
-
// Delete corrupted models info
|
|
186
|
-
await CactusFileSystem.deleteFile(CactusLM.modelsInfoPath);
|
|
187
|
-
}
|
|
228
|
+
public async getModels(): Promise<CactusModel[]> {
|
|
229
|
+
if (CactusLM.cactusModelsCache) {
|
|
230
|
+
return CactusLM.cactusModelsCache;
|
|
188
231
|
}
|
|
189
|
-
|
|
190
232
|
const models = await Database.getModels();
|
|
191
|
-
|
|
192
233
|
for (const model of models) {
|
|
193
234
|
model.isDownloaded = await CactusFileSystem.modelExists(model.slug);
|
|
194
235
|
}
|
|
195
|
-
|
|
196
|
-
await CactusFileSystem.writeFile(
|
|
197
|
-
CactusLM.modelsInfoPath,
|
|
198
|
-
JSON.stringify(models)
|
|
199
|
-
);
|
|
200
|
-
|
|
236
|
+
CactusLM.cactusModelsCache = models;
|
|
201
237
|
return models;
|
|
202
238
|
}
|
|
203
239
|
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { Cactus, CactusFileSystem } from '../native';
|
|
2
|
+
import type {
|
|
3
|
+
CactusSTTDownloadParams,
|
|
4
|
+
CactusSTTTranscribeParams,
|
|
5
|
+
CactusSTTTranscribeResult,
|
|
6
|
+
CactusSTTParams,
|
|
7
|
+
CactusSTTAudioEmbedParams,
|
|
8
|
+
CactusSTTAudioEmbedResult,
|
|
9
|
+
} from '../types/CactusSTT';
|
|
10
|
+
import type { CactusModel } from '../types/CactusModel';
|
|
11
|
+
import { Telemetry } from '../telemetry/Telemetry';
|
|
12
|
+
import { CactusConfig } from '../config/CactusConfig';
|
|
13
|
+
import { Database } from '../api/Database';
|
|
14
|
+
import { getErrorMessage } from '../utils/error';
|
|
15
|
+
|
|
16
|
+
export class CactusSTT {
|
|
17
|
+
private readonly cactus = new Cactus();
|
|
18
|
+
|
|
19
|
+
private readonly model: string;
|
|
20
|
+
private readonly contextSize: number;
|
|
21
|
+
|
|
22
|
+
private isDownloading = false;
|
|
23
|
+
private isInitialized = false;
|
|
24
|
+
private isGenerating = false;
|
|
25
|
+
|
|
26
|
+
private static readonly defaultModel = 'whisper-small';
|
|
27
|
+
private static readonly defaultContextSize = 2048;
|
|
28
|
+
private static readonly defaultTranscribeOptions = {
|
|
29
|
+
maxTokens: 512,
|
|
30
|
+
};
|
|
31
|
+
private static readonly defaultEmbedBufferSize = 32768;
|
|
32
|
+
|
|
33
|
+
private static cactusModelsCache: CactusModel[] | null = null;
|
|
34
|
+
|
|
35
|
+
constructor({ model, contextSize }: CactusSTTParams = {}) {
|
|
36
|
+
Telemetry.init(CactusConfig.telemetryToken);
|
|
37
|
+
|
|
38
|
+
this.model = model ?? CactusSTT.defaultModel;
|
|
39
|
+
this.contextSize = contextSize ?? CactusSTT.defaultContextSize;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public async download({
|
|
43
|
+
onProgress,
|
|
44
|
+
}: CactusSTTDownloadParams = {}): Promise<void> {
|
|
45
|
+
if (this.isDownloading) {
|
|
46
|
+
throw new Error('CactusSTT is already downloading');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (await CactusFileSystem.modelExists(this.model)) {
|
|
50
|
+
onProgress?.(1.0);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
this.isDownloading = true;
|
|
55
|
+
try {
|
|
56
|
+
await CactusFileSystem.downloadModel(
|
|
57
|
+
this.model,
|
|
58
|
+
`https://vlqqczxwyaodtcdmdmlw.supabase.co/storage/v1/object/public/voice-models/${this.model}.zip`,
|
|
59
|
+
onProgress
|
|
60
|
+
);
|
|
61
|
+
} finally {
|
|
62
|
+
this.isDownloading = false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public async init(): Promise<void> {
|
|
67
|
+
if (this.isInitialized) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!(await CactusFileSystem.modelExists(this.model))) {
|
|
72
|
+
throw new Error(`Model "${this.model}" is not downloaded`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const modelPath = await CactusFileSystem.getModelPath(this.model);
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
await this.cactus.init(modelPath, this.contextSize);
|
|
79
|
+
Telemetry.logInit(this.model, true);
|
|
80
|
+
this.isInitialized = true;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
Telemetry.logInit(this.model, false, getErrorMessage(error));
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
public async transcribe({
|
|
88
|
+
audioFilePath,
|
|
89
|
+
prompt = '<|startoftranscript|><|en|><|transcribe|><|notimestamps|>',
|
|
90
|
+
options,
|
|
91
|
+
onToken,
|
|
92
|
+
}: CactusSTTTranscribeParams): Promise<CactusSTTTranscribeResult> {
|
|
93
|
+
if (this.isGenerating) {
|
|
94
|
+
throw new Error('CactusSTT is already generating');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
await this.init();
|
|
98
|
+
|
|
99
|
+
options = { ...CactusSTT.defaultTranscribeOptions, ...options };
|
|
100
|
+
const responseBufferSize = 32768;
|
|
101
|
+
|
|
102
|
+
this.isGenerating = true;
|
|
103
|
+
try {
|
|
104
|
+
const result = await this.cactus.transcribe(
|
|
105
|
+
audioFilePath,
|
|
106
|
+
prompt,
|
|
107
|
+
responseBufferSize,
|
|
108
|
+
options,
|
|
109
|
+
onToken
|
|
110
|
+
);
|
|
111
|
+
Telemetry.logTranscribe(
|
|
112
|
+
this.model,
|
|
113
|
+
result.success,
|
|
114
|
+
result.success ? undefined : result.response,
|
|
115
|
+
result
|
|
116
|
+
);
|
|
117
|
+
return result;
|
|
118
|
+
} catch (error) {
|
|
119
|
+
Telemetry.logTranscribe(this.model, false, getErrorMessage(error));
|
|
120
|
+
throw error;
|
|
121
|
+
} finally {
|
|
122
|
+
this.isGenerating = false;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
public async audioEmbed({
|
|
127
|
+
audioPath,
|
|
128
|
+
}: CactusSTTAudioEmbedParams): Promise<CactusSTTAudioEmbedResult> {
|
|
129
|
+
if (this.isGenerating) {
|
|
130
|
+
throw new Error('CactusSTT is already generating');
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
await this.init();
|
|
134
|
+
|
|
135
|
+
this.isGenerating = true;
|
|
136
|
+
try {
|
|
137
|
+
const embedding = await this.cactus.audioEmbed(
|
|
138
|
+
audioPath,
|
|
139
|
+
CactusSTT.defaultEmbedBufferSize
|
|
140
|
+
);
|
|
141
|
+
Telemetry.logAudioEmbedding(this.model, true);
|
|
142
|
+
return { embedding };
|
|
143
|
+
} catch (error) {
|
|
144
|
+
Telemetry.logAudioEmbedding(this.model, false, getErrorMessage(error));
|
|
145
|
+
throw error;
|
|
146
|
+
} finally {
|
|
147
|
+
this.isGenerating = false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
public stop(): Promise<void> {
|
|
152
|
+
return this.cactus.stop();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
public async reset(): Promise<void> {
|
|
156
|
+
await this.stop();
|
|
157
|
+
return this.cactus.reset();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
public async destroy(): Promise<void> {
|
|
161
|
+
if (!this.isInitialized) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
await this.stop();
|
|
166
|
+
await this.cactus.destroy();
|
|
167
|
+
|
|
168
|
+
this.isInitialized = false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
public async getModels(): Promise<CactusModel[]> {
|
|
172
|
+
if (CactusSTT.cactusModelsCache) {
|
|
173
|
+
return CactusSTT.cactusModelsCache;
|
|
174
|
+
}
|
|
175
|
+
const models = await Database.getModels();
|
|
176
|
+
for (const model of models) {
|
|
177
|
+
model.isDownloaded = await CactusFileSystem.modelExists(model.slug);
|
|
178
|
+
}
|
|
179
|
+
CactusSTT.cactusModelsCache = models;
|
|
180
|
+
return models;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const packageVersion = '1.0
|
|
1
|
+
export const packageVersion = '1.1.0';
|
package/src/hooks/useCactusLM.ts
CHANGED
|
@@ -7,7 +7,8 @@ import type {
|
|
|
7
7
|
CactusLMCompleteResult,
|
|
8
8
|
CactusLMEmbedParams,
|
|
9
9
|
CactusLMEmbedResult,
|
|
10
|
-
|
|
10
|
+
CactusLMImageEmbedParams,
|
|
11
|
+
CactusLMImageEmbedResult,
|
|
11
12
|
CactusLMCompleteParams,
|
|
12
13
|
CactusLMDownloadParams,
|
|
13
14
|
} from '../types/CactusLM';
|
|
@@ -16,9 +17,10 @@ import type { CactusModel } from '../types/CactusModel';
|
|
|
16
17
|
export const useCactusLM = ({
|
|
17
18
|
model = 'qwen3-0.6',
|
|
18
19
|
contextSize = 2048,
|
|
20
|
+
corpusDir = undefined,
|
|
19
21
|
}: CactusLMParams = {}) => {
|
|
20
22
|
const [cactusLM, setCactusLM] = useState(
|
|
21
|
-
() => new CactusLM({ model, contextSize })
|
|
23
|
+
() => new CactusLM({ model, contextSize, corpusDir })
|
|
22
24
|
);
|
|
23
25
|
|
|
24
26
|
// State
|
|
@@ -38,7 +40,7 @@ export const useCactusLM = ({
|
|
|
38
40
|
}, [model]);
|
|
39
41
|
|
|
40
42
|
useEffect(() => {
|
|
41
|
-
setCactusLM(new CactusLM({ model, contextSize }));
|
|
43
|
+
setCactusLM(new CactusLM({ model, contextSize, corpusDir }));
|
|
42
44
|
|
|
43
45
|
setCompletion('');
|
|
44
46
|
setIsGenerating(false);
|
|
@@ -67,7 +69,7 @@ export const useCactusLM = ({
|
|
|
67
69
|
return () => {
|
|
68
70
|
mounted = false;
|
|
69
71
|
};
|
|
70
|
-
}, [model, contextSize]);
|
|
72
|
+
}, [model, contextSize, corpusDir]);
|
|
71
73
|
|
|
72
74
|
useEffect(() => {
|
|
73
75
|
return () => {
|
|
@@ -83,10 +85,15 @@ export const useCactusLM = ({
|
|
|
83
85
|
throw new Error(message);
|
|
84
86
|
}
|
|
85
87
|
|
|
88
|
+
setError(null);
|
|
89
|
+
|
|
90
|
+
if (isDownloaded) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
86
94
|
const thisModel = currentModelRef.current;
|
|
87
95
|
const thisDownloadId = ++currentDownloadIdRef.current;
|
|
88
96
|
|
|
89
|
-
setError(null);
|
|
90
97
|
setDownloadProgress(0);
|
|
91
98
|
setIsDownloading(true);
|
|
92
99
|
try {
|
|
@@ -134,7 +141,7 @@ export const useCactusLM = ({
|
|
|
134
141
|
setDownloadProgress(0);
|
|
135
142
|
}
|
|
136
143
|
},
|
|
137
|
-
[cactusLM, isDownloading]
|
|
144
|
+
[cactusLM, isDownloading, isDownloaded]
|
|
138
145
|
);
|
|
139
146
|
|
|
140
147
|
const init = useCallback(async () => {
|
|
@@ -162,6 +169,7 @@ export const useCactusLM = ({
|
|
|
162
169
|
options,
|
|
163
170
|
tools,
|
|
164
171
|
onToken,
|
|
172
|
+
mode,
|
|
165
173
|
}: CactusLMCompleteParams): Promise<CactusLMCompleteResult> => {
|
|
166
174
|
if (isGenerating) {
|
|
167
175
|
const message = 'CactusLM is already generating';
|
|
@@ -181,6 +189,7 @@ export const useCactusLM = ({
|
|
|
181
189
|
setCompletion((prev) => prev + token);
|
|
182
190
|
onToken?.(token);
|
|
183
191
|
},
|
|
192
|
+
mode,
|
|
184
193
|
});
|
|
185
194
|
} catch (e) {
|
|
186
195
|
setError(getErrorMessage(e));
|
|
@@ -214,6 +223,30 @@ export const useCactusLM = ({
|
|
|
214
223
|
[cactusLM, isGenerating]
|
|
215
224
|
);
|
|
216
225
|
|
|
226
|
+
const imageEmbed = useCallback(
|
|
227
|
+
async ({
|
|
228
|
+
imagePath,
|
|
229
|
+
}: CactusLMImageEmbedParams): Promise<CactusLMImageEmbedResult> => {
|
|
230
|
+
if (isGenerating) {
|
|
231
|
+
const message = 'CactusLM is already generating';
|
|
232
|
+
setError(message);
|
|
233
|
+
throw new Error(message);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
setError(null);
|
|
237
|
+
setIsGenerating(true);
|
|
238
|
+
try {
|
|
239
|
+
return await cactusLM.imageEmbed({ imagePath });
|
|
240
|
+
} catch (e) {
|
|
241
|
+
setError(getErrorMessage(e));
|
|
242
|
+
throw e;
|
|
243
|
+
} finally {
|
|
244
|
+
setIsGenerating(false);
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
[cactusLM, isGenerating]
|
|
248
|
+
);
|
|
249
|
+
|
|
217
250
|
const stop = useCallback(async () => {
|
|
218
251
|
setError(null);
|
|
219
252
|
try {
|
|
@@ -226,40 +259,37 @@ export const useCactusLM = ({
|
|
|
226
259
|
|
|
227
260
|
const reset = useCallback(async () => {
|
|
228
261
|
setError(null);
|
|
229
|
-
setCompletion('');
|
|
230
262
|
try {
|
|
231
263
|
await cactusLM.reset();
|
|
232
264
|
} catch (e) {
|
|
233
265
|
setError(getErrorMessage(e));
|
|
234
266
|
throw e;
|
|
267
|
+
} finally {
|
|
268
|
+
setCompletion('');
|
|
235
269
|
}
|
|
236
270
|
}, [cactusLM]);
|
|
237
271
|
|
|
238
272
|
const destroy = useCallback(async () => {
|
|
239
273
|
setError(null);
|
|
240
|
-
setCompletion('');
|
|
241
274
|
try {
|
|
242
275
|
await cactusLM.destroy();
|
|
243
276
|
} catch (e) {
|
|
244
277
|
setError(getErrorMessage(e));
|
|
245
278
|
throw e;
|
|
279
|
+
} finally {
|
|
280
|
+
setCompletion('');
|
|
246
281
|
}
|
|
247
282
|
}, [cactusLM]);
|
|
248
283
|
|
|
249
|
-
const getModels = useCallback(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
throw e;
|
|
259
|
-
}
|
|
260
|
-
},
|
|
261
|
-
[cactusLM]
|
|
262
|
-
);
|
|
284
|
+
const getModels = useCallback(async (): Promise<CactusModel[]> => {
|
|
285
|
+
setError(null);
|
|
286
|
+
try {
|
|
287
|
+
return await cactusLM.getModels();
|
|
288
|
+
} catch (e) {
|
|
289
|
+
setError(getErrorMessage(e));
|
|
290
|
+
throw e;
|
|
291
|
+
}
|
|
292
|
+
}, [cactusLM]);
|
|
263
293
|
|
|
264
294
|
return {
|
|
265
295
|
completion,
|
|
@@ -274,6 +304,7 @@ export const useCactusLM = ({
|
|
|
274
304
|
init,
|
|
275
305
|
complete,
|
|
276
306
|
embed,
|
|
307
|
+
imageEmbed,
|
|
277
308
|
reset,
|
|
278
309
|
stop,
|
|
279
310
|
destroy,
|