@mulingai-npm/redis 3.29.0 → 3.29.2
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.
|
@@ -6,9 +6,7 @@ export interface ChunkLogOptions {
|
|
|
6
6
|
language?: boolean;
|
|
7
7
|
processLength?: boolean;
|
|
8
8
|
finalTranscription?: boolean;
|
|
9
|
-
|
|
10
|
-
audioFilePath?: boolean;
|
|
11
|
-
stt?: boolean;
|
|
9
|
+
streamingChunk?: boolean;
|
|
12
10
|
translation?: boolean;
|
|
13
11
|
tts?: boolean;
|
|
14
12
|
label?: string;
|
|
@@ -5,13 +5,11 @@ const mulingstream_chunk_manager_1 = require("../managers/mulingstream-chunk-man
|
|
|
5
5
|
const defaults = {
|
|
6
6
|
chunkId: false,
|
|
7
7
|
chunkNumber: true,
|
|
8
|
-
audioFilePath: true,
|
|
9
8
|
roomId: false,
|
|
10
9
|
processLength: false,
|
|
11
10
|
language: false,
|
|
12
11
|
finalTranscription: true,
|
|
13
|
-
|
|
14
|
-
stt: false,
|
|
12
|
+
streamingChunk: false,
|
|
15
13
|
translation: false,
|
|
16
14
|
tts: true,
|
|
17
15
|
label: ''
|
|
@@ -62,18 +60,14 @@ class MulingstreamChunkLogger extends mulingstream_chunk_manager_1.MulingstreamC
|
|
|
62
60
|
out.roomId = chunk.roomId;
|
|
63
61
|
if (o.chunkNumber)
|
|
64
62
|
out.chunkNumber = chunk.chunkNumber;
|
|
65
|
-
if (o.
|
|
66
|
-
out.
|
|
63
|
+
if (o.streamingChunk)
|
|
64
|
+
out.streamingChunk = chunk.streamingChunk;
|
|
67
65
|
if (o.processLength)
|
|
68
66
|
out.processLength = Date.now() - chunk.createdAt + 'ms';
|
|
69
67
|
if (o.language)
|
|
70
68
|
out.language = chunk.language;
|
|
71
69
|
if (o.finalTranscription)
|
|
72
70
|
out.finalTranscription = chunk.finalTranscription;
|
|
73
|
-
if (o.sttStatus)
|
|
74
|
-
out.sttStatus = chunk.sttStatus;
|
|
75
|
-
if (o.stt)
|
|
76
|
-
out.stt = chunk.stt;
|
|
77
71
|
if (o.translation)
|
|
78
72
|
out.translation = chunk.translation;
|
|
79
73
|
if (o.tts)
|
|
@@ -1,36 +1,30 @@
|
|
|
1
1
|
import { RedisClient } from '../redis-client';
|
|
2
2
|
export type StepStatus = 'INIT' | 'DISCARDED' | 'READY' | 'USED';
|
|
3
|
-
export type SttService = 'azure' | '
|
|
3
|
+
export type SttService = 'azure' | 'deepgram';
|
|
4
4
|
export type SttProvider = {
|
|
5
5
|
service: SttService;
|
|
6
6
|
model?: string;
|
|
7
7
|
};
|
|
8
|
+
export type StreamingChunkData = {
|
|
9
|
+
startMs: number;
|
|
10
|
+
endMs: number;
|
|
11
|
+
durationMs: number;
|
|
12
|
+
confidence: number;
|
|
13
|
+
wordCount: number;
|
|
14
|
+
speechFinal: boolean;
|
|
15
|
+
provider: string;
|
|
16
|
+
model?: string;
|
|
17
|
+
};
|
|
8
18
|
export type MulingstreamChunkData = {
|
|
9
19
|
chunkId: string;
|
|
10
20
|
roomId: string;
|
|
11
21
|
chunkNumber: number;
|
|
12
22
|
language: string;
|
|
13
|
-
sttProviders: SttProvider[];
|
|
14
23
|
targetLanguages: string[];
|
|
15
24
|
shortCodeTargetLanguages: string[];
|
|
16
25
|
finalTranscription: string;
|
|
17
|
-
sttStatus: StepStatus;
|
|
18
26
|
createdAt: number;
|
|
19
|
-
|
|
20
|
-
start: number;
|
|
21
|
-
end: number;
|
|
22
|
-
duration: number;
|
|
23
|
-
isFirst: boolean;
|
|
24
|
-
isLast: boolean;
|
|
25
|
-
audioFilePath: string;
|
|
26
|
-
};
|
|
27
|
-
stt: {
|
|
28
|
-
[service: string]: {
|
|
29
|
-
transcription: string;
|
|
30
|
-
model?: string;
|
|
31
|
-
status: StepStatus;
|
|
32
|
-
};
|
|
33
|
-
};
|
|
27
|
+
streamingChunk: StreamingChunkData;
|
|
34
28
|
translation: {
|
|
35
29
|
[language: string]: {
|
|
36
30
|
translation: string;
|
|
@@ -60,39 +54,31 @@ export declare class MulingstreamChunkManager {
|
|
|
60
54
|
getRooms(): Promise<string[]>;
|
|
61
55
|
getMulingstreamChunksByRoom(roomId: string): Promise<MulingstreamChunkData[] | null>;
|
|
62
56
|
getRoomById(roomId: string): Promise<MulingstreamChunkData[]>;
|
|
63
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Add a streaming chunk with STT already completed
|
|
59
|
+
* This is the primary method for streaming-based transcription
|
|
60
|
+
*/
|
|
61
|
+
addStreamingChunk(params: {
|
|
64
62
|
roomId: string;
|
|
65
63
|
chunkNumber: number;
|
|
66
64
|
language: string;
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
duration: number;
|
|
70
|
-
isFirst: boolean;
|
|
71
|
-
isLast: boolean;
|
|
72
|
-
sttProviders: SttProvider[];
|
|
65
|
+
transcription: string;
|
|
66
|
+
sttProvider: SttProvider;
|
|
73
67
|
targetLanguages: string[];
|
|
74
68
|
shortCodeTargetLanguages: string[];
|
|
69
|
+
streamingData: {
|
|
70
|
+
startMs: number;
|
|
71
|
+
endMs: number;
|
|
72
|
+
durationMs: number;
|
|
73
|
+
confidence: number;
|
|
74
|
+
wordCount: number;
|
|
75
|
+
speechFinal: boolean;
|
|
76
|
+
};
|
|
75
77
|
}): Promise<MulingstreamChunkData>;
|
|
76
78
|
private getChunkId;
|
|
77
79
|
getMulingstreamChunkById(roomId: string, n: number): Promise<MulingstreamChunkData>;
|
|
78
80
|
private withChunk;
|
|
79
|
-
|
|
80
|
-
updateStt(roomId: string, n: number, service: SttService, opt: {
|
|
81
|
-
transcription?: string;
|
|
82
|
-
sttStatus?: StepStatus;
|
|
83
|
-
}): Promise<MulingstreamChunkData | null>;
|
|
84
|
-
updateSttObject(roomId: string, n: number, newStt: Record<string, {
|
|
85
|
-
transcription: string;
|
|
86
|
-
model?: string;
|
|
87
|
-
status: StepStatus;
|
|
88
|
-
}>): Promise<MulingstreamChunkData | null>;
|
|
89
|
-
discardStt(roomId: string, n: number): Promise<MulingstreamChunkData | null>;
|
|
90
|
-
updateSttAsUsed(roomId: string, chunkNumber: number): Promise<MulingstreamChunkData | null>;
|
|
91
|
-
updateFinalTranscription(roomId: string, n: number, opt: {
|
|
92
|
-
transcription?: string;
|
|
93
|
-
sttStatus?: StepStatus;
|
|
94
|
-
}): Promise<MulingstreamChunkData | null>;
|
|
95
|
-
discardPostStt(roomId: string, n: number): Promise<MulingstreamChunkData | null>;
|
|
81
|
+
updateFinalTranscription(roomId: string, n: number, transcription: string): Promise<MulingstreamChunkData | null>;
|
|
96
82
|
discardLanguage(roomId: string, n: number, lang: string): Promise<MulingstreamChunkData | null>;
|
|
97
83
|
discardLanguages(roomId: string, n: number, opt: {
|
|
98
84
|
translation?: string[];
|
|
@@ -29,19 +29,16 @@ class MulingstreamChunkManager {
|
|
|
29
29
|
roomId: h.roomId,
|
|
30
30
|
chunkNumber: parseInt(h.chunkNumber, 10),
|
|
31
31
|
language: h.language,
|
|
32
|
-
sttProviders: this.deserialize(h.sttProviders),
|
|
33
32
|
targetLanguages: this.deserialize(h.targetLanguages),
|
|
34
33
|
shortCodeTargetLanguages: this.deserialize(h.shortCodeTargetLanguages),
|
|
35
34
|
finalTranscription: h.finalTranscription,
|
|
36
|
-
sttStatus: h.sttStatus,
|
|
37
35
|
createdAt: parseInt(h.createdAt, 10),
|
|
38
|
-
|
|
39
|
-
stt: this.deserialize(h.stt),
|
|
36
|
+
streamingChunk: this.deserialize(h.streamingChunk),
|
|
40
37
|
translation: this.deserialize(h.translation),
|
|
41
38
|
tts: this.deserialize(h.tts)
|
|
42
39
|
};
|
|
43
40
|
}
|
|
44
|
-
getTimeout(
|
|
41
|
+
getTimeout() {
|
|
45
42
|
return 25000;
|
|
46
43
|
}
|
|
47
44
|
async initRoom(roomId) {
|
|
@@ -69,9 +66,14 @@ class MulingstreamChunkManager {
|
|
|
69
66
|
getRoomById(roomId) {
|
|
70
67
|
return this.getMulingstreamChunksByRoom(roomId);
|
|
71
68
|
}
|
|
72
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Add a streaming chunk with STT already completed
|
|
71
|
+
* This is the primary method for streaming-based transcription
|
|
72
|
+
*/
|
|
73
|
+
async addStreamingChunk(params) {
|
|
73
74
|
var _a, _b;
|
|
74
|
-
const { roomId, chunkNumber, language,
|
|
75
|
+
const { roomId, chunkNumber, language, transcription, sttProvider, targetLanguages, shortCodeTargetLanguages, streamingData } = params;
|
|
76
|
+
// Clear room if this is first chunk (new session)
|
|
75
77
|
if (chunkNumber === 1) {
|
|
76
78
|
const old = await this.redisClient.zrange(this.roomZsetKey(roomId), 0, -1);
|
|
77
79
|
if (old.length) {
|
|
@@ -82,6 +84,7 @@ class MulingstreamChunkManager {
|
|
|
82
84
|
}
|
|
83
85
|
}
|
|
84
86
|
else {
|
|
87
|
+
// Remove duplicate chunk numbers
|
|
85
88
|
const dup = await this.redisClient.zrangebyscore(this.roomZsetKey(roomId), chunkNumber, chunkNumber);
|
|
86
89
|
if (dup.length) {
|
|
87
90
|
const p = this.redisClient.pipeline();
|
|
@@ -91,13 +94,20 @@ class MulingstreamChunkManager {
|
|
|
91
94
|
}
|
|
92
95
|
}
|
|
93
96
|
const chunkId = this.generateChunkId(roomId, chunkNumber);
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
+
// Streaming chunk data (includes STT provider info)
|
|
98
|
+
const streamingChunk = {
|
|
99
|
+
startMs: streamingData.startMs,
|
|
100
|
+
endMs: streamingData.endMs,
|
|
101
|
+
durationMs: streamingData.durationMs,
|
|
102
|
+
confidence: streamingData.confidence,
|
|
103
|
+
wordCount: streamingData.wordCount,
|
|
104
|
+
speechFinal: streamingData.speechFinal,
|
|
105
|
+
provider: sttProvider.service,
|
|
106
|
+
model: sttProvider.model
|
|
107
|
+
};
|
|
97
108
|
const translation = {};
|
|
98
109
|
const tts = {};
|
|
99
|
-
// Use ISO codes (targetLanguages) for translation/tts keys
|
|
100
|
-
// This ensures consistency throughout the pipeline - we can always derive short codes from ISO codes when needed
|
|
110
|
+
// Use ISO codes (targetLanguages) for translation/tts keys
|
|
101
111
|
targetLanguages.forEach((l) => {
|
|
102
112
|
translation[l] = { translation: '', status: 'INIT' };
|
|
103
113
|
tts[l] = { ttsAudioPath: '', status: 'INIT', isEmitted: false };
|
|
@@ -108,14 +118,11 @@ class MulingstreamChunkManager {
|
|
|
108
118
|
roomId,
|
|
109
119
|
chunkNumber,
|
|
110
120
|
language,
|
|
111
|
-
sttProviders,
|
|
112
121
|
targetLanguages,
|
|
113
122
|
shortCodeTargetLanguages,
|
|
114
|
-
finalTranscription:
|
|
115
|
-
sttStatus: 'INIT',
|
|
123
|
+
finalTranscription: transcription,
|
|
116
124
|
createdAt,
|
|
117
|
-
|
|
118
|
-
stt,
|
|
125
|
+
streamingChunk,
|
|
119
126
|
translation,
|
|
120
127
|
tts
|
|
121
128
|
};
|
|
@@ -124,14 +131,11 @@ class MulingstreamChunkManager {
|
|
|
124
131
|
roomId,
|
|
125
132
|
chunkNumber: String(chunkNumber),
|
|
126
133
|
language,
|
|
127
|
-
sttProviders: this.serialize(sttProviders),
|
|
128
134
|
targetLanguages: this.serialize(targetLanguages),
|
|
129
135
|
shortCodeTargetLanguages: this.serialize(shortCodeTargetLanguages),
|
|
130
|
-
finalTranscription:
|
|
131
|
-
sttStatus: 'INIT',
|
|
136
|
+
finalTranscription: transcription,
|
|
132
137
|
createdAt: String(createdAt),
|
|
133
|
-
|
|
134
|
-
stt: this.serialize(stt),
|
|
138
|
+
streamingChunk: this.serialize(streamingChunk),
|
|
135
139
|
translation: this.serialize(translation),
|
|
136
140
|
tts: this.serialize(tts)
|
|
137
141
|
};
|
|
@@ -143,6 +147,7 @@ class MulingstreamChunkManager {
|
|
|
143
147
|
pipe.zrange(this.roomZsetKey(roomId), 0, -ROOM_ARRAY_LENGTH - 1, 'WITHSCORES');
|
|
144
148
|
const execResults = await pipe.exec();
|
|
145
149
|
const oldPairs = ((_b = (_a = execResults === null || execResults === void 0 ? void 0 : execResults[4]) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : []);
|
|
150
|
+
// Trim old chunks if room exceeds max length
|
|
146
151
|
if (Array.isArray(oldPairs) && oldPairs.length) {
|
|
147
152
|
const oldIds = [];
|
|
148
153
|
for (let i = 0; i < oldPairs.length; i += 2)
|
|
@@ -178,65 +183,17 @@ class MulingstreamChunkManager {
|
|
|
178
183
|
const p = this.redisClient.pipeline();
|
|
179
184
|
p.hset(key, {
|
|
180
185
|
finalTranscription: chunk.finalTranscription,
|
|
181
|
-
sttStatus: chunk.sttStatus,
|
|
182
|
-
stt: this.serialize(chunk.stt),
|
|
183
186
|
translation: this.serialize(chunk.translation),
|
|
184
187
|
tts: this.serialize(chunk.tts),
|
|
185
|
-
|
|
188
|
+
streamingChunk: this.serialize(chunk.streamingChunk)
|
|
186
189
|
});
|
|
187
190
|
p.expire(key, EXPIRATION);
|
|
188
191
|
await p.exec();
|
|
189
192
|
return chunk;
|
|
190
193
|
}
|
|
191
|
-
async
|
|
192
|
-
return this.withChunk(roomId, chunkNumber, (chunk) => {
|
|
193
|
-
chunk.audioChunk.audioFilePath = audioFilePath;
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
async updateStt(roomId, n, service, opt) {
|
|
197
|
-
return this.withChunk(roomId, n, (c) => {
|
|
198
|
-
if (!c.stt[service])
|
|
199
|
-
return;
|
|
200
|
-
if (opt.transcription !== undefined)
|
|
201
|
-
c.stt[service].transcription = opt.transcription;
|
|
202
|
-
if (opt.sttStatus !== undefined)
|
|
203
|
-
c.stt[service].status = opt.sttStatus;
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
async updateSttObject(roomId, n, newStt) {
|
|
207
|
-
return this.withChunk(roomId, n, (c) => {
|
|
208
|
-
c.stt = newStt;
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
async discardStt(roomId, n) {
|
|
212
|
-
return this.withChunk(roomId, n, (c) => {
|
|
213
|
-
Object.keys(c.stt).forEach((p) => {
|
|
214
|
-
c.stt[p].transcription = '';
|
|
215
|
-
c.stt[p].status = 'DISCARDED';
|
|
216
|
-
});
|
|
217
|
-
c.finalTranscription = '';
|
|
218
|
-
c.sttStatus = 'DISCARDED';
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
async updateSttAsUsed(roomId, chunkNumber) {
|
|
222
|
-
return this.withChunk(roomId, chunkNumber, (chunk) => {
|
|
223
|
-
Object.keys(chunk.stt).forEach((sttProvider) => {
|
|
224
|
-
chunk.stt[sttProvider].status = 'USED';
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
async updateFinalTranscription(roomId, n, opt) {
|
|
229
|
-
return this.withChunk(roomId, n, (c) => {
|
|
230
|
-
if (opt.transcription !== undefined)
|
|
231
|
-
c.finalTranscription = opt.transcription;
|
|
232
|
-
if (opt.sttStatus !== undefined)
|
|
233
|
-
c.sttStatus = opt.sttStatus;
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
async discardPostStt(roomId, n) {
|
|
194
|
+
async updateFinalTranscription(roomId, n, transcription) {
|
|
237
195
|
return this.withChunk(roomId, n, (c) => {
|
|
238
|
-
|
|
239
|
-
Object.values(c.tts).forEach((t) => (t.status = 'DISCARDED'));
|
|
196
|
+
c.finalTranscription = transcription;
|
|
240
197
|
});
|
|
241
198
|
}
|
|
242
199
|
async discardLanguage(roomId, n, lang) {
|
|
@@ -348,9 +305,8 @@ class MulingstreamChunkManager {
|
|
|
348
305
|
const now = Date.now();
|
|
349
306
|
for (let i = lastDone + 1; i < window.length; i++) {
|
|
350
307
|
const entry = window[i].tts[lang];
|
|
351
|
-
const audio = window[i].audioChunk;
|
|
352
308
|
if (entry.status === 'INIT') {
|
|
353
|
-
if (now - window[i].createdAt > this.getTimeout(
|
|
309
|
+
if (now - window[i].createdAt > this.getTimeout())
|
|
354
310
|
entry.status = 'DISCARDED';
|
|
355
311
|
else
|
|
356
312
|
blocked = true;
|