cactus-react-native 1.4.0 → 1.5.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 +212 -27
- package/android/src/main/jniLibs/arm64-v8a/libcactus.a +0 -0
- package/cpp/HybridCactus.cpp +119 -0
- package/cpp/HybridCactus.hpp +13 -0
- package/cpp/cactus_ffi.h +24 -0
- package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus_ffi.h +24 -0
- package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/cactus_utils.h +41 -1
- package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/engine.h +66 -48
- package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/gemma_tools.h +549 -0
- package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/graph.h +102 -21
- package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/kernel.h +45 -195
- package/ios/cactus.xcframework/ios-arm64/cactus.framework/Headers/kernel_utils.h +399 -140
- package/ios/cactus.xcframework/ios-arm64/cactus.framework/cactus +0 -0
- package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus_ffi.h +24 -0
- package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/cactus_utils.h +41 -1
- package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/engine.h +66 -48
- package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/gemma_tools.h +549 -0
- package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/graph.h +102 -21
- package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/kernel.h +45 -195
- package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/Headers/kernel_utils.h +399 -140
- package/ios/cactus.xcframework/ios-arm64-simulator/cactus.framework/cactus +0 -0
- package/lib/module/api/Database.js +0 -92
- package/lib/module/api/Database.js.map +1 -1
- package/lib/module/classes/CactusLM.js +33 -15
- package/lib/module/classes/CactusLM.js.map +1 -1
- package/lib/module/classes/CactusSTT.js +90 -15
- package/lib/module/classes/CactusSTT.js.map +1 -1
- package/lib/module/hooks/useCactusLM.js +14 -5
- package/lib/module/hooks/useCactusLM.js.map +1 -1
- package/lib/module/hooks/useCactusSTT.js +100 -4
- package/lib/module/hooks/useCactusSTT.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/models.js +336 -0
- package/lib/module/models.js.map +1 -0
- package/lib/module/native/Cactus.js +37 -0
- package/lib/module/native/Cactus.js.map +1 -1
- package/lib/module/types/CactusLM.js +2 -0
- package/lib/module/types/CactusSTT.js +2 -0
- package/lib/module/types/common.js +2 -0
- package/lib/module/types/{CactusModel.js.map → common.js.map} +1 -1
- package/lib/typescript/src/api/Database.d.ts +0 -6
- package/lib/typescript/src/api/Database.d.ts.map +1 -1
- package/lib/typescript/src/classes/CactusLM.d.ts +7 -3
- package/lib/typescript/src/classes/CactusLM.d.ts.map +1 -1
- package/lib/typescript/src/classes/CactusSTT.d.ts +13 -4
- package/lib/typescript/src/classes/CactusSTT.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useCactusLM.d.ts +2 -2
- package/lib/typescript/src/hooks/useCactusLM.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useCactusSTT.d.ts +12 -4
- package/lib/typescript/src/hooks/useCactusSTT.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/models.d.ts +6 -0
- package/lib/typescript/src/models.d.ts.map +1 -0
- package/lib/typescript/src/native/Cactus.d.ts +6 -1
- package/lib/typescript/src/native/Cactus.d.ts.map +1 -1
- package/lib/typescript/src/specs/Cactus.nitro.d.ts +5 -0
- package/lib/typescript/src/specs/Cactus.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types/CactusLM.d.ts +2 -0
- package/lib/typescript/src/types/CactusLM.d.ts.map +1 -1
- package/lib/typescript/src/types/CactusSTT.d.ts +20 -0
- package/lib/typescript/src/types/CactusSTT.d.ts.map +1 -1
- package/lib/typescript/src/types/common.d.ts +28 -0
- package/lib/typescript/src/types/common.d.ts.map +1 -0
- package/nitrogen/generated/shared/c++/HybridCactusSpec.cpp +5 -0
- package/nitrogen/generated/shared/c++/HybridCactusSpec.hpp +5 -0
- package/package.json +1 -1
- package/src/api/Database.ts +0 -133
- package/src/classes/CactusLM.ts +49 -17
- package/src/classes/CactusSTT.ts +118 -17
- package/src/hooks/useCactusLM.ts +25 -5
- package/src/hooks/useCactusSTT.ts +117 -5
- package/src/index.tsx +6 -2
- package/src/models.ts +344 -0
- package/src/native/Cactus.ts +55 -0
- package/src/specs/Cactus.nitro.ts +5 -0
- package/src/types/CactusLM.ts +3 -0
- package/src/types/CactusSTT.ts +26 -0
- package/src/types/common.ts +28 -0
- package/lib/module/types/CactusModel.js +0 -2
- package/lib/module/types/CactusSTTModel.js +0 -2
- package/lib/module/types/CactusSTTModel.js.map +0 -1
- package/lib/typescript/src/types/CactusModel.d.ts +0 -13
- package/lib/typescript/src/types/CactusModel.d.ts.map +0 -1
- package/lib/typescript/src/types/CactusSTTModel.d.ts +0 -8
- package/lib/typescript/src/types/CactusSTTModel.d.ts.map +0 -1
- package/src/types/CactusModel.ts +0 -15
- package/src/types/CactusSTTModel.ts +0 -10
package/src/classes/CactusLM.ts
CHANGED
|
@@ -13,12 +13,12 @@ import type {
|
|
|
13
13
|
CactusLMImageEmbedResult,
|
|
14
14
|
CactusLMParams,
|
|
15
15
|
} from '../types/CactusLM';
|
|
16
|
-
import type { CactusModel } from '../types/CactusModel';
|
|
17
16
|
import { Telemetry } from '../telemetry/Telemetry';
|
|
18
17
|
import { CactusConfig } from '../config/CactusConfig';
|
|
19
|
-
import { Database } from '../api/Database';
|
|
20
18
|
import { getErrorMessage } from '../utils/error';
|
|
21
19
|
import { RemoteLM } from '../api/RemoteLM';
|
|
20
|
+
import models from '../models';
|
|
21
|
+
import type { CactusModel } from '../types/common';
|
|
22
22
|
|
|
23
23
|
export class CactusLM {
|
|
24
24
|
private readonly cactus = new Cactus();
|
|
@@ -26,25 +26,46 @@ export class CactusLM {
|
|
|
26
26
|
private readonly model: string;
|
|
27
27
|
private readonly contextSize: number;
|
|
28
28
|
private readonly corpusDir?: string;
|
|
29
|
+
private readonly options: {
|
|
30
|
+
quantization: 'int4' | 'int8';
|
|
31
|
+
pro: boolean;
|
|
32
|
+
};
|
|
29
33
|
|
|
30
34
|
private isDownloading = false;
|
|
31
35
|
private isInitialized = false;
|
|
32
36
|
private isGenerating = false;
|
|
33
37
|
|
|
34
|
-
private static readonly defaultModel = 'qwen3-0.
|
|
38
|
+
private static readonly defaultModel = 'qwen3-0.6b';
|
|
35
39
|
private static readonly defaultContextSize = 2048;
|
|
40
|
+
private static readonly defaultOptions = {
|
|
41
|
+
quantization: 'int4' as const,
|
|
42
|
+
pro: false,
|
|
43
|
+
};
|
|
44
|
+
private static readonly quantizationExceptions: {
|
|
45
|
+
[model: string]: 'int4' | 'int8';
|
|
46
|
+
} = {
|
|
47
|
+
'gemma-3-270m-it': 'int8' as const,
|
|
48
|
+
'functiongemma-270m-it': 'int8' as const,
|
|
49
|
+
};
|
|
36
50
|
private static readonly defaultCompleteOptions = {
|
|
37
51
|
maxTokens: 512,
|
|
38
52
|
};
|
|
39
53
|
private static readonly defaultCompleteMode = 'local';
|
|
40
54
|
private static readonly defaultEmbedBufferSize = 2048;
|
|
41
55
|
|
|
42
|
-
constructor({ model, contextSize, corpusDir }: CactusLMParams = {}) {
|
|
56
|
+
constructor({ model, contextSize, corpusDir, options }: CactusLMParams = {}) {
|
|
43
57
|
Telemetry.init(CactusConfig.telemetryToken);
|
|
44
58
|
|
|
45
59
|
this.model = model ?? CactusLM.defaultModel;
|
|
46
60
|
this.contextSize = contextSize ?? CactusLM.defaultContextSize;
|
|
47
61
|
this.corpusDir = corpusDir;
|
|
62
|
+
this.options = {
|
|
63
|
+
quantization:
|
|
64
|
+
options?.quantization ??
|
|
65
|
+
CactusLM.quantizationExceptions[this.model] ??
|
|
66
|
+
CactusLM.defaultOptions.quantization,
|
|
67
|
+
pro: options?.pro ?? CactusLM.defaultOptions.pro,
|
|
68
|
+
};
|
|
48
69
|
}
|
|
49
70
|
|
|
50
71
|
public async download({
|
|
@@ -59,17 +80,25 @@ export class CactusLM {
|
|
|
59
80
|
throw new Error('CactusLM is already downloading');
|
|
60
81
|
}
|
|
61
82
|
|
|
62
|
-
if (await CactusFileSystem.modelExists(this.
|
|
83
|
+
if (await CactusFileSystem.modelExists(this.getModelName())) {
|
|
84
|
+
console.log('Model already exists', this.getModelName());
|
|
63
85
|
onProgress?.(1.0);
|
|
64
86
|
return;
|
|
65
87
|
}
|
|
66
88
|
|
|
67
89
|
this.isDownloading = true;
|
|
68
90
|
try {
|
|
69
|
-
const
|
|
91
|
+
const modelConfig =
|
|
92
|
+
models[this.model]?.quantization[this.options.quantization];
|
|
93
|
+
const url = this.options.pro ? modelConfig?.pro?.apple : modelConfig?.url;
|
|
94
|
+
|
|
95
|
+
if (!url) {
|
|
96
|
+
throw new Error(`Model ${this.model} with specified options not found`);
|
|
97
|
+
}
|
|
98
|
+
|
|
70
99
|
await CactusFileSystem.downloadModel(
|
|
71
|
-
this.
|
|
72
|
-
|
|
100
|
+
this.getModelName(),
|
|
101
|
+
url,
|
|
73
102
|
onProgress
|
|
74
103
|
);
|
|
75
104
|
} finally {
|
|
@@ -86,10 +115,13 @@ export class CactusLM {
|
|
|
86
115
|
if (this.isModelPath(this.model)) {
|
|
87
116
|
modelPath = this.model.replace('file://', '');
|
|
88
117
|
} else {
|
|
89
|
-
if (!(await CactusFileSystem.modelExists(this.
|
|
90
|
-
|
|
118
|
+
if (!(await CactusFileSystem.modelExists(this.getModelName()))) {
|
|
119
|
+
console.log('Model not found:', this.getModelName());
|
|
120
|
+
throw new Error(
|
|
121
|
+
`Model "${this.model}" with options ${JSON.stringify(this.options)} is not downloaded`
|
|
122
|
+
);
|
|
91
123
|
}
|
|
92
|
-
modelPath = await CactusFileSystem.getModelPath(this.
|
|
124
|
+
modelPath = await CactusFileSystem.getModelPath(this.getModelName());
|
|
93
125
|
}
|
|
94
126
|
|
|
95
127
|
try {
|
|
@@ -255,15 +287,15 @@ export class CactusLM {
|
|
|
255
287
|
this.isInitialized = false;
|
|
256
288
|
}
|
|
257
289
|
|
|
258
|
-
public
|
|
259
|
-
|
|
260
|
-
for (const model of models) {
|
|
261
|
-
model.isDownloaded = await CactusFileSystem.modelExists(model.slug);
|
|
262
|
-
}
|
|
263
|
-
return models;
|
|
290
|
+
public getModels(): CactusModel[] {
|
|
291
|
+
return Object.values(models).filter((model) => model.completion);
|
|
264
292
|
}
|
|
265
293
|
|
|
266
294
|
private isModelPath(model: string): boolean {
|
|
267
295
|
return model.startsWith('file://') || model.startsWith('/');
|
|
268
296
|
}
|
|
297
|
+
|
|
298
|
+
private getModelName(): string {
|
|
299
|
+
return `${this.model}-${this.options.quantization}${this.options.pro ? '-pro' : ''}`;
|
|
300
|
+
}
|
|
269
301
|
}
|
package/src/classes/CactusSTT.ts
CHANGED
|
@@ -6,37 +6,56 @@ import type {
|
|
|
6
6
|
CactusSTTParams,
|
|
7
7
|
CactusSTTAudioEmbedParams,
|
|
8
8
|
CactusSTTAudioEmbedResult,
|
|
9
|
+
CactusSTTStreamTranscribeInsertParams,
|
|
10
|
+
CactusSTTStreamTranscribeProcessParams,
|
|
11
|
+
CactusSTTStreamTranscribeProcessResult,
|
|
12
|
+
CactusSTTStreamTranscribeFinalizeResult,
|
|
9
13
|
} from '../types/CactusSTT';
|
|
10
14
|
import { Telemetry } from '../telemetry/Telemetry';
|
|
11
15
|
import { CactusConfig } from '../config/CactusConfig';
|
|
12
|
-
import { Database } from '../api/Database';
|
|
13
16
|
import { getErrorMessage } from '../utils/error';
|
|
14
|
-
import
|
|
17
|
+
import models from '../models';
|
|
18
|
+
import type { CactusModel } from '../types/common';
|
|
15
19
|
|
|
16
20
|
export class CactusSTT {
|
|
17
21
|
private readonly cactus = new Cactus();
|
|
18
22
|
|
|
19
23
|
private readonly model: string;
|
|
20
24
|
private readonly contextSize: number;
|
|
25
|
+
private readonly options: {
|
|
26
|
+
quantization: 'int4' | 'int8';
|
|
27
|
+
pro: boolean;
|
|
28
|
+
};
|
|
21
29
|
|
|
22
30
|
private isDownloading = false;
|
|
23
31
|
private isInitialized = false;
|
|
24
32
|
private isGenerating = false;
|
|
25
33
|
|
|
34
|
+
private isStreamTranscribeInitialized = false;
|
|
35
|
+
|
|
26
36
|
private static readonly defaultModel = 'whisper-small';
|
|
27
37
|
private static readonly defaultContextSize = 2048;
|
|
38
|
+
private static readonly defaultOptions = {
|
|
39
|
+
quantization: 'int4' as const,
|
|
40
|
+
pro: false,
|
|
41
|
+
};
|
|
28
42
|
private static readonly defaultPrompt =
|
|
29
43
|
'<|startoftranscript|><|en|><|transcribe|><|notimestamps|>';
|
|
30
44
|
private static readonly defaultTranscribeOptions = {
|
|
31
|
-
maxTokens:
|
|
45
|
+
maxTokens: 384,
|
|
32
46
|
};
|
|
33
47
|
private static readonly defaultEmbedBufferSize = 4096;
|
|
34
48
|
|
|
35
|
-
constructor({ model, contextSize }: CactusSTTParams = {}) {
|
|
49
|
+
constructor({ model, contextSize, options }: CactusSTTParams = {}) {
|
|
36
50
|
Telemetry.init(CactusConfig.telemetryToken);
|
|
37
51
|
|
|
38
52
|
this.model = model ?? CactusSTT.defaultModel;
|
|
39
53
|
this.contextSize = contextSize ?? CactusSTT.defaultContextSize;
|
|
54
|
+
this.options = {
|
|
55
|
+
quantization:
|
|
56
|
+
options?.quantization ?? CactusSTT.defaultOptions.quantization,
|
|
57
|
+
pro: options?.pro ?? CactusSTT.defaultOptions.pro,
|
|
58
|
+
};
|
|
40
59
|
}
|
|
41
60
|
|
|
42
61
|
public async download({
|
|
@@ -51,17 +70,25 @@ export class CactusSTT {
|
|
|
51
70
|
throw new Error('CactusSTT is already downloading');
|
|
52
71
|
}
|
|
53
72
|
|
|
54
|
-
if (await CactusFileSystem.modelExists(this.
|
|
73
|
+
if (await CactusFileSystem.modelExists(this.getModelName())) {
|
|
74
|
+
console.log('Model already exists', this.getModelName());
|
|
55
75
|
onProgress?.(1.0);
|
|
56
76
|
return;
|
|
57
77
|
}
|
|
58
78
|
|
|
59
79
|
this.isDownloading = true;
|
|
60
80
|
try {
|
|
61
|
-
const
|
|
81
|
+
const modelConfig =
|
|
82
|
+
models[this.model]?.quantization[this.options.quantization];
|
|
83
|
+
const url = this.options.pro ? modelConfig?.pro?.apple : modelConfig?.url;
|
|
84
|
+
|
|
85
|
+
if (!url) {
|
|
86
|
+
throw new Error(`Model ${this.model} with specified options not found`);
|
|
87
|
+
}
|
|
88
|
+
|
|
62
89
|
await CactusFileSystem.downloadModel(
|
|
63
|
-
this.
|
|
64
|
-
|
|
90
|
+
this.getModelName(),
|
|
91
|
+
url,
|
|
65
92
|
onProgress
|
|
66
93
|
);
|
|
67
94
|
} finally {
|
|
@@ -78,10 +105,13 @@ export class CactusSTT {
|
|
|
78
105
|
if (this.isModelPath(this.model)) {
|
|
79
106
|
modelPath = this.model.replace('file://', '');
|
|
80
107
|
} else {
|
|
81
|
-
if (!(await CactusFileSystem.modelExists(this.
|
|
82
|
-
|
|
108
|
+
if (!(await CactusFileSystem.modelExists(this.getModelName()))) {
|
|
109
|
+
console.log('Model does not exist', this.getModelName());
|
|
110
|
+
throw new Error(
|
|
111
|
+
`Model "${this.model}" with options ${JSON.stringify(this.options)} is not downloaded`
|
|
112
|
+
);
|
|
83
113
|
}
|
|
84
|
-
modelPath = await CactusFileSystem.getModelPath(this.
|
|
114
|
+
modelPath = await CactusFileSystem.getModelPath(this.getModelName());
|
|
85
115
|
}
|
|
86
116
|
|
|
87
117
|
try {
|
|
@@ -137,6 +167,76 @@ export class CactusSTT {
|
|
|
137
167
|
}
|
|
138
168
|
}
|
|
139
169
|
|
|
170
|
+
public async streamTranscribeInit(): Promise<void> {
|
|
171
|
+
if (this.isStreamTranscribeInitialized) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
await this.init();
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
await this.cactus.streamTranscribeInit();
|
|
179
|
+
this.isStreamTranscribeInitialized = true;
|
|
180
|
+
} catch (error) {
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
public async streamTranscribeInsert({
|
|
186
|
+
audio,
|
|
187
|
+
}: CactusSTTStreamTranscribeInsertParams): Promise<void> {
|
|
188
|
+
if (!this.isStreamTranscribeInitialized) {
|
|
189
|
+
throw new Error('CactusSTT stream transcribe is not initialized');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
await this.cactus.streamTranscribeInsert(audio);
|
|
194
|
+
} catch (error) {
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
public async streamTranscribeProcess({
|
|
200
|
+
options,
|
|
201
|
+
}: CactusSTTStreamTranscribeProcessParams = {}): Promise<CactusSTTStreamTranscribeProcessResult> {
|
|
202
|
+
if (!this.isStreamTranscribeInitialized) {
|
|
203
|
+
throw new Error('CactusSTT stream transcribe is not initialized');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const result = await this.cactus.streamTranscribeProcess(options);
|
|
208
|
+
return result;
|
|
209
|
+
} catch (error) {
|
|
210
|
+
throw error;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
public async streamTranscribeFinalize(): Promise<CactusSTTStreamTranscribeFinalizeResult> {
|
|
215
|
+
if (!this.isStreamTranscribeInitialized) {
|
|
216
|
+
throw new Error('CactusSTT stream transcribe is not initialized');
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
const result = await this.cactus.streamTranscribeFinalize();
|
|
221
|
+
return result;
|
|
222
|
+
} catch (error) {
|
|
223
|
+
throw error;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
public async streamTranscribeDestroy(): Promise<void> {
|
|
228
|
+
if (!this.isStreamTranscribeInitialized) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
try {
|
|
233
|
+
await this.cactus.streamTranscribeDestroy();
|
|
234
|
+
this.isStreamTranscribeInitialized = false;
|
|
235
|
+
} catch (error) {
|
|
236
|
+
throw error;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
140
240
|
public async audioEmbed({
|
|
141
241
|
audioPath,
|
|
142
242
|
}: CactusSTTAudioEmbedParams): Promise<CactusSTTAudioEmbedResult> {
|
|
@@ -177,20 +277,21 @@ export class CactusSTT {
|
|
|
177
277
|
}
|
|
178
278
|
|
|
179
279
|
await this.stop();
|
|
280
|
+
await this.streamTranscribeDestroy();
|
|
180
281
|
await this.cactus.destroy();
|
|
181
282
|
|
|
182
283
|
this.isInitialized = false;
|
|
183
284
|
}
|
|
184
285
|
|
|
185
|
-
public
|
|
186
|
-
|
|
187
|
-
for (const model of models) {
|
|
188
|
-
model.isDownloaded = await CactusFileSystem.modelExists(model.slug);
|
|
189
|
-
}
|
|
190
|
-
return models;
|
|
286
|
+
public getModels(): CactusModel[] {
|
|
287
|
+
return Object.values(models).filter((model) => model.speech);
|
|
191
288
|
}
|
|
192
289
|
|
|
193
290
|
private isModelPath(model: string): boolean {
|
|
194
291
|
return model.startsWith('file://') || model.startsWith('/');
|
|
195
292
|
}
|
|
293
|
+
|
|
294
|
+
private getModelName(): string {
|
|
295
|
+
return `${this.model}-${this.options.quantization}${this.options.pro ? '-pro' : ''}`;
|
|
296
|
+
}
|
|
196
297
|
}
|
package/src/hooks/useCactusLM.ts
CHANGED
|
@@ -16,15 +16,19 @@ import type {
|
|
|
16
16
|
CactusLMImageEmbedResult,
|
|
17
17
|
CactusLMDownloadParams,
|
|
18
18
|
} from '../types/CactusLM';
|
|
19
|
-
import type { CactusModel } from '../types/
|
|
19
|
+
import type { CactusModel } from '../types/common';
|
|
20
20
|
|
|
21
21
|
export const useCactusLM = ({
|
|
22
|
-
model = 'qwen3-0.
|
|
22
|
+
model = 'qwen3-0.6b',
|
|
23
23
|
contextSize = 2048,
|
|
24
24
|
corpusDir = undefined,
|
|
25
|
+
options: modelOptions = {
|
|
26
|
+
quantization: undefined,
|
|
27
|
+
pro: false,
|
|
28
|
+
},
|
|
25
29
|
}: CactusLMParams = {}) => {
|
|
26
30
|
const [cactusLM, setCactusLM] = useState(
|
|
27
|
-
() => new CactusLM({ model, contextSize, corpusDir })
|
|
31
|
+
() => new CactusLM({ model, contextSize, corpusDir, options: modelOptions })
|
|
28
32
|
);
|
|
29
33
|
|
|
30
34
|
// State
|
|
@@ -44,7 +48,17 @@ export const useCactusLM = ({
|
|
|
44
48
|
}, [model]);
|
|
45
49
|
|
|
46
50
|
useEffect(() => {
|
|
47
|
-
setCactusLM(
|
|
51
|
+
setCactusLM(
|
|
52
|
+
new CactusLM({
|
|
53
|
+
model,
|
|
54
|
+
contextSize,
|
|
55
|
+
corpusDir,
|
|
56
|
+
options: {
|
|
57
|
+
quantization: modelOptions.quantization,
|
|
58
|
+
pro: modelOptions.pro,
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
);
|
|
48
62
|
|
|
49
63
|
setCompletion('');
|
|
50
64
|
setIsGenerating(false);
|
|
@@ -73,7 +87,13 @@ export const useCactusLM = ({
|
|
|
73
87
|
return () => {
|
|
74
88
|
mounted = false;
|
|
75
89
|
};
|
|
76
|
-
}, [
|
|
90
|
+
}, [
|
|
91
|
+
model,
|
|
92
|
+
contextSize,
|
|
93
|
+
corpusDir,
|
|
94
|
+
modelOptions.quantization,
|
|
95
|
+
modelOptions.pro,
|
|
96
|
+
]);
|
|
77
97
|
|
|
78
98
|
useEffect(() => {
|
|
79
99
|
return () => {
|
|
@@ -9,20 +9,32 @@ import type {
|
|
|
9
9
|
CactusSTTDownloadParams,
|
|
10
10
|
CactusSTTAudioEmbedParams,
|
|
11
11
|
CactusSTTAudioEmbedResult,
|
|
12
|
+
CactusSTTStreamTranscribeInsertParams,
|
|
13
|
+
CactusSTTStreamTranscribeProcessParams,
|
|
14
|
+
CactusSTTStreamTranscribeProcessResult,
|
|
15
|
+
CactusSTTStreamTranscribeFinalizeResult,
|
|
12
16
|
} from '../types/CactusSTT';
|
|
13
|
-
import type {
|
|
17
|
+
import type { CactusModel } from '../types/common';
|
|
14
18
|
|
|
15
19
|
export const useCactusSTT = ({
|
|
16
20
|
model = 'whisper-small',
|
|
17
21
|
contextSize = 2048,
|
|
22
|
+
options: modelOptions = {
|
|
23
|
+
quantization: undefined,
|
|
24
|
+
pro: false,
|
|
25
|
+
},
|
|
18
26
|
}: CactusSTTParams = {}) => {
|
|
19
27
|
const [cactusSTT, setCactusSTT] = useState(
|
|
20
|
-
() => new CactusSTT({ model, contextSize })
|
|
28
|
+
() => new CactusSTT({ model, contextSize, options: modelOptions })
|
|
21
29
|
);
|
|
22
30
|
|
|
23
31
|
// State
|
|
24
32
|
const [transcription, setTranscription] = useState('');
|
|
33
|
+
const [streamTranscribeConfirmed, setStreamTranscribeConfirmed] =
|
|
34
|
+
useState('');
|
|
35
|
+
const [streamTranscribePending, setStreamTranscribePending] = useState('');
|
|
25
36
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
37
|
+
const [isStreamTranscribing, setIsStreamTranscribing] = useState(false);
|
|
26
38
|
const [isInitializing, setIsInitializing] = useState(false);
|
|
27
39
|
const [isDownloaded, setIsDownloaded] = useState(false);
|
|
28
40
|
const [isDownloading, setIsDownloading] = useState(false);
|
|
@@ -37,10 +49,22 @@ export const useCactusSTT = ({
|
|
|
37
49
|
}, [model]);
|
|
38
50
|
|
|
39
51
|
useEffect(() => {
|
|
40
|
-
setCactusSTT(
|
|
52
|
+
setCactusSTT(
|
|
53
|
+
new CactusSTT({
|
|
54
|
+
model,
|
|
55
|
+
contextSize,
|
|
56
|
+
options: {
|
|
57
|
+
quantization: modelOptions.quantization,
|
|
58
|
+
pro: modelOptions.pro,
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
);
|
|
41
62
|
|
|
42
63
|
setTranscription('');
|
|
64
|
+
setStreamTranscribeConfirmed('');
|
|
65
|
+
setStreamTranscribePending('');
|
|
43
66
|
setIsGenerating(false);
|
|
67
|
+
setIsStreamTranscribing(false);
|
|
44
68
|
setIsInitializing(false);
|
|
45
69
|
setIsDownloaded(false);
|
|
46
70
|
setIsDownloading(false);
|
|
@@ -66,7 +90,7 @@ export const useCactusSTT = ({
|
|
|
66
90
|
return () => {
|
|
67
91
|
mounted = false;
|
|
68
92
|
};
|
|
69
|
-
}, [model, contextSize]);
|
|
93
|
+
}, [model, contextSize, modelOptions.quantization, modelOptions.pro]);
|
|
70
94
|
|
|
71
95
|
useEffect(() => {
|
|
72
96
|
return () => {
|
|
@@ -220,6 +244,83 @@ export const useCactusSTT = ({
|
|
|
220
244
|
[cactusSTT, isGenerating]
|
|
221
245
|
);
|
|
222
246
|
|
|
247
|
+
const streamTranscribeInit = useCallback(async () => {
|
|
248
|
+
if (isStreamTranscribing) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
setError(null);
|
|
253
|
+
setStreamTranscribeConfirmed('');
|
|
254
|
+
setStreamTranscribePending('');
|
|
255
|
+
setIsStreamTranscribing(true);
|
|
256
|
+
try {
|
|
257
|
+
await cactusSTT.streamTranscribeInit();
|
|
258
|
+
} catch (e) {
|
|
259
|
+
setError(getErrorMessage(e));
|
|
260
|
+
setIsStreamTranscribing(false);
|
|
261
|
+
throw e;
|
|
262
|
+
}
|
|
263
|
+
}, [cactusSTT, isStreamTranscribing]);
|
|
264
|
+
|
|
265
|
+
const streamTranscribeInsert = useCallback(
|
|
266
|
+
async ({ audio }: CactusSTTStreamTranscribeInsertParams): Promise<void> => {
|
|
267
|
+
setError(null);
|
|
268
|
+
try {
|
|
269
|
+
await cactusSTT.streamTranscribeInsert({ audio });
|
|
270
|
+
} catch (e) {
|
|
271
|
+
setError(getErrorMessage(e));
|
|
272
|
+
throw e;
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
[cactusSTT]
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
const streamTranscribeProcess = useCallback(
|
|
279
|
+
async ({
|
|
280
|
+
options,
|
|
281
|
+
}: CactusSTTStreamTranscribeProcessParams = {}): Promise<CactusSTTStreamTranscribeProcessResult> => {
|
|
282
|
+
setError(null);
|
|
283
|
+
try {
|
|
284
|
+
const result = await cactusSTT.streamTranscribeProcess({ options });
|
|
285
|
+
setStreamTranscribeConfirmed((prev) => prev + result.confirmed);
|
|
286
|
+
setStreamTranscribePending(result.pending);
|
|
287
|
+
return result;
|
|
288
|
+
} catch (e) {
|
|
289
|
+
setError(getErrorMessage(e));
|
|
290
|
+
throw e;
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
[cactusSTT]
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
const streamTranscribeFinalize =
|
|
297
|
+
useCallback(async (): Promise<CactusSTTStreamTranscribeFinalizeResult> => {
|
|
298
|
+
setError(null);
|
|
299
|
+
try {
|
|
300
|
+
const result = await cactusSTT.streamTranscribeFinalize();
|
|
301
|
+
setStreamTranscribeConfirmed((prev) => prev + result.confirmed);
|
|
302
|
+
setStreamTranscribePending('');
|
|
303
|
+
setIsStreamTranscribing(false);
|
|
304
|
+
return result;
|
|
305
|
+
} catch (e) {
|
|
306
|
+
setError(getErrorMessage(e));
|
|
307
|
+
throw e;
|
|
308
|
+
}
|
|
309
|
+
}, [cactusSTT]);
|
|
310
|
+
|
|
311
|
+
const streamTranscribeDestroy = useCallback(async (): Promise<void> => {
|
|
312
|
+
setError(null);
|
|
313
|
+
try {
|
|
314
|
+
await cactusSTT.streamTranscribeDestroy();
|
|
315
|
+
} catch (e) {
|
|
316
|
+
setError(getErrorMessage(e));
|
|
317
|
+
throw e;
|
|
318
|
+
} finally {
|
|
319
|
+
setIsStreamTranscribing(false);
|
|
320
|
+
setStreamTranscribePending('');
|
|
321
|
+
}
|
|
322
|
+
}, [cactusSTT]);
|
|
323
|
+
|
|
223
324
|
const stop = useCallback(async () => {
|
|
224
325
|
setError(null);
|
|
225
326
|
try {
|
|
@@ -251,10 +352,13 @@ export const useCactusSTT = ({
|
|
|
251
352
|
throw e;
|
|
252
353
|
} finally {
|
|
253
354
|
setTranscription('');
|
|
355
|
+
setStreamTranscribeConfirmed('');
|
|
356
|
+
setStreamTranscribePending('');
|
|
357
|
+
setIsStreamTranscribing(false);
|
|
254
358
|
}
|
|
255
359
|
}, [cactusSTT]);
|
|
256
360
|
|
|
257
|
-
const getModels = useCallback(async (): Promise<
|
|
361
|
+
const getModels = useCallback(async (): Promise<CactusModel[]> => {
|
|
258
362
|
setError(null);
|
|
259
363
|
try {
|
|
260
364
|
return await cactusSTT.getModels();
|
|
@@ -266,7 +370,10 @@ export const useCactusSTT = ({
|
|
|
266
370
|
|
|
267
371
|
return {
|
|
268
372
|
transcription,
|
|
373
|
+
streamTranscribeConfirmed,
|
|
374
|
+
streamTranscribePending,
|
|
269
375
|
isGenerating,
|
|
376
|
+
isStreamTranscribing,
|
|
270
377
|
isInitializing,
|
|
271
378
|
isDownloaded,
|
|
272
379
|
isDownloading,
|
|
@@ -277,6 +384,11 @@ export const useCactusSTT = ({
|
|
|
277
384
|
init,
|
|
278
385
|
transcribe,
|
|
279
386
|
audioEmbed,
|
|
387
|
+
streamTranscribeInit,
|
|
388
|
+
streamTranscribeInsert,
|
|
389
|
+
streamTranscribeProcess,
|
|
390
|
+
streamTranscribeFinalize,
|
|
391
|
+
streamTranscribeDestroy,
|
|
280
392
|
reset,
|
|
281
393
|
stop,
|
|
282
394
|
destroy,
|
package/src/index.tsx
CHANGED
|
@@ -9,8 +9,7 @@ export { useCactusSTT } from './hooks/useCactusSTT';
|
|
|
9
9
|
export { useCactusIndex } from './hooks/useCactusIndex';
|
|
10
10
|
|
|
11
11
|
// Types
|
|
12
|
-
export type { CactusModel } from './types/
|
|
13
|
-
export type { CactusSTTModel } from './types/CactusSTTModel';
|
|
12
|
+
export type { CactusModel, ModelOptions } from './types/common';
|
|
14
13
|
export type {
|
|
15
14
|
CactusLMParams,
|
|
16
15
|
CactusLMDownloadParams,
|
|
@@ -36,6 +35,11 @@ export type {
|
|
|
36
35
|
CactusSTTTranscribeResult,
|
|
37
36
|
CactusSTTAudioEmbedParams,
|
|
38
37
|
CactusSTTAudioEmbedResult,
|
|
38
|
+
CactusSTTStreamTranscribeInsertParams,
|
|
39
|
+
StreamTranscribeProcessOptions,
|
|
40
|
+
CactusSTTStreamTranscribeProcessParams,
|
|
41
|
+
CactusSTTStreamTranscribeProcessResult,
|
|
42
|
+
CactusSTTStreamTranscribeFinalizeResult,
|
|
39
43
|
} from './types/CactusSTT';
|
|
40
44
|
export type {
|
|
41
45
|
CactusIndexParams,
|