@mulingai-npm/redis 3.29.1 → 3.29.3

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
- sttStatus?: boolean;
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
- sttStatus: true,
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.audioFilePath)
66
- out.audioFilePath = chunk.audioChunk.audioFilePath;
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' | 'whisper' | 'deepgram';
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
- audioChunk: {
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;
@@ -45,6 +39,8 @@ export type MulingstreamChunkData = {
45
39
  duration?: number;
46
40
  };
47
41
  };
42
+ isComplete: boolean;
43
+ isSaved: boolean;
48
44
  };
49
45
  export declare class MulingstreamChunkManager {
50
46
  private redisClient;
@@ -60,39 +56,31 @@ export declare class MulingstreamChunkManager {
60
56
  getRooms(): Promise<string[]>;
61
57
  getMulingstreamChunksByRoom(roomId: string): Promise<MulingstreamChunkData[] | null>;
62
58
  getRoomById(roomId: string): Promise<MulingstreamChunkData[]>;
63
- addMulingstreamChunk(params: {
59
+ /**
60
+ * Add a streaming chunk with STT already completed
61
+ * This is the primary method for streaming-based transcription
62
+ */
63
+ addStreamingChunk(params: {
64
64
  roomId: string;
65
65
  chunkNumber: number;
66
66
  language: string;
67
- start: number;
68
- end: number;
69
- duration: number;
70
- isFirst: boolean;
71
- isLast: boolean;
72
- sttProviders: SttProvider[];
67
+ transcription: string;
68
+ sttProvider: SttProvider;
73
69
  targetLanguages: string[];
74
70
  shortCodeTargetLanguages: string[];
71
+ streamingData: {
72
+ startMs: number;
73
+ endMs: number;
74
+ durationMs: number;
75
+ confidence: number;
76
+ wordCount: number;
77
+ speechFinal: boolean;
78
+ };
75
79
  }): Promise<MulingstreamChunkData>;
76
80
  private getChunkId;
77
81
  getMulingstreamChunkById(roomId: string, n: number): Promise<MulingstreamChunkData>;
78
82
  private withChunk;
79
- updateAudioFilePath(roomId: string, chunkNumber: number, audioFilePath: string): Promise<MulingstreamChunkData | null>;
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>;
83
+ updateFinalTranscription(roomId: string, n: number, transcription: string): Promise<MulingstreamChunkData | null>;
96
84
  discardLanguage(roomId: string, n: number, lang: string): Promise<MulingstreamChunkData | null>;
97
85
  discardLanguages(roomId: string, n: number, opt: {
98
86
  translation?: string[];
@@ -110,6 +98,26 @@ export declare class MulingstreamChunkManager {
110
98
  duration?: number;
111
99
  }): Promise<MulingstreamChunkData | null>;
112
100
  areTranslationsProcessed(roomId: string, n: number): Promise<boolean>;
101
+ /**
102
+ * Check if all TTS for all target languages are done (READY, USED, or DISCARDED - not INIT)
103
+ */
104
+ isChunkComplete(roomId: string, n: number): Promise<boolean>;
105
+ /**
106
+ * Mark a chunk as complete (all TTS finished for all target languages)
107
+ */
108
+ markChunkComplete(roomId: string, n: number): Promise<MulingstreamChunkData | null>;
109
+ /**
110
+ * Mark a chunk as saved to database
111
+ */
112
+ markChunkSaved(roomId: string, n: number): Promise<MulingstreamChunkData | null>;
113
+ /**
114
+ * Get all complete but unsaved chunks for a room
115
+ */
116
+ getCompleteUnsavedChunks(roomId: string): Promise<MulingstreamChunkData[]>;
117
+ /**
118
+ * Mark multiple chunks as saved in batch
119
+ */
120
+ markChunksSaved(roomId: string, chunkNumbers: number[]): Promise<void>;
113
121
  /**
114
122
  * @deprecated This function is replaced by ChunkSequencer (5-sequencer-handler.ts)
115
123
  *
@@ -29,19 +29,18 @@ 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
- audioChunk: this.deserialize(h.audioChunk),
39
- stt: this.deserialize(h.stt),
36
+ streamingChunk: this.deserialize(h.streamingChunk),
40
37
  translation: this.deserialize(h.translation),
41
- tts: this.deserialize(h.tts)
38
+ tts: this.deserialize(h.tts),
39
+ isComplete: h.isComplete === 'true',
40
+ isSaved: h.isSaved === 'true'
42
41
  };
43
42
  }
44
- getTimeout(start, end) {
43
+ getTimeout() {
45
44
  return 25000;
46
45
  }
47
46
  async initRoom(roomId) {
@@ -69,9 +68,14 @@ class MulingstreamChunkManager {
69
68
  getRoomById(roomId) {
70
69
  return this.getMulingstreamChunksByRoom(roomId);
71
70
  }
72
- async addMulingstreamChunk(params) {
71
+ /**
72
+ * Add a streaming chunk with STT already completed
73
+ * This is the primary method for streaming-based transcription
74
+ */
75
+ async addStreamingChunk(params) {
73
76
  var _a, _b;
74
- const { roomId, chunkNumber, language, start, end, duration, isFirst, isLast, sttProviders, targetLanguages, shortCodeTargetLanguages } = params;
77
+ const { roomId, chunkNumber, language, transcription, sttProvider, targetLanguages, shortCodeTargetLanguages, streamingData } = params;
78
+ // Clear room if this is first chunk (new session)
75
79
  if (chunkNumber === 1) {
76
80
  const old = await this.redisClient.zrange(this.roomZsetKey(roomId), 0, -1);
77
81
  if (old.length) {
@@ -82,6 +86,7 @@ class MulingstreamChunkManager {
82
86
  }
83
87
  }
84
88
  else {
89
+ // Remove duplicate chunk numbers
85
90
  const dup = await this.redisClient.zrangebyscore(this.roomZsetKey(roomId), chunkNumber, chunkNumber);
86
91
  if (dup.length) {
87
92
  const p = this.redisClient.pipeline();
@@ -91,13 +96,20 @@ class MulingstreamChunkManager {
91
96
  }
92
97
  }
93
98
  const chunkId = this.generateChunkId(roomId, chunkNumber);
94
- const audioChunk = { start, end, duration, isFirst, isLast, audioFilePath: '' };
95
- const stt = {};
96
- sttProviders.forEach((p) => (stt[p.service] = { transcription: '', model: p.model, status: 'INIT' }));
99
+ // Streaming chunk data (includes STT provider info)
100
+ const streamingChunk = {
101
+ startMs: streamingData.startMs,
102
+ endMs: streamingData.endMs,
103
+ durationMs: streamingData.durationMs,
104
+ confidence: streamingData.confidence,
105
+ wordCount: streamingData.wordCount,
106
+ speechFinal: streamingData.speechFinal,
107
+ provider: sttProvider.service,
108
+ model: sttProvider.model
109
+ };
97
110
  const translation = {};
98
111
  const tts = {};
99
- // Use ISO codes (targetLanguages) for translation/tts keys, NOT short codes
100
- // This ensures consistency throughout the pipeline - we can always derive short codes from ISO codes when needed
112
+ // Use ISO codes (targetLanguages) for translation/tts keys
101
113
  targetLanguages.forEach((l) => {
102
114
  translation[l] = { translation: '', status: 'INIT' };
103
115
  tts[l] = { ttsAudioPath: '', status: 'INIT', isEmitted: false };
@@ -108,32 +120,30 @@ class MulingstreamChunkManager {
108
120
  roomId,
109
121
  chunkNumber,
110
122
  language,
111
- sttProviders,
112
123
  targetLanguages,
113
124
  shortCodeTargetLanguages,
114
- finalTranscription: '',
115
- sttStatus: 'INIT',
125
+ finalTranscription: transcription,
116
126
  createdAt,
117
- audioChunk,
118
- stt,
127
+ streamingChunk,
119
128
  translation,
120
- tts
129
+ tts,
130
+ isComplete: false,
131
+ isSaved: false
121
132
  };
122
133
  const hash = {
123
134
  chunkId,
124
135
  roomId,
125
136
  chunkNumber: String(chunkNumber),
126
137
  language,
127
- sttProviders: this.serialize(sttProviders),
128
138
  targetLanguages: this.serialize(targetLanguages),
129
139
  shortCodeTargetLanguages: this.serialize(shortCodeTargetLanguages),
130
- finalTranscription: '',
131
- sttStatus: 'INIT',
140
+ finalTranscription: transcription,
132
141
  createdAt: String(createdAt),
133
- audioChunk: this.serialize(audioChunk),
134
- stt: this.serialize(stt),
142
+ streamingChunk: this.serialize(streamingChunk),
135
143
  translation: this.serialize(translation),
136
- tts: this.serialize(tts)
144
+ tts: this.serialize(tts),
145
+ isComplete: 'false',
146
+ isSaved: 'false'
137
147
  };
138
148
  const pipe = this.redisClient.pipeline();
139
149
  pipe.hset(this.chunkHashKey(chunkId), hash);
@@ -143,6 +153,7 @@ class MulingstreamChunkManager {
143
153
  pipe.zrange(this.roomZsetKey(roomId), 0, -ROOM_ARRAY_LENGTH - 1, 'WITHSCORES');
144
154
  const execResults = await pipe.exec();
145
155
  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 : []);
156
+ // Trim old chunks if room exceeds max length
146
157
  if (Array.isArray(oldPairs) && oldPairs.length) {
147
158
  const oldIds = [];
148
159
  for (let i = 0; i < oldPairs.length; i += 2)
@@ -178,65 +189,19 @@ class MulingstreamChunkManager {
178
189
  const p = this.redisClient.pipeline();
179
190
  p.hset(key, {
180
191
  finalTranscription: chunk.finalTranscription,
181
- sttStatus: chunk.sttStatus,
182
- stt: this.serialize(chunk.stt),
183
192
  translation: this.serialize(chunk.translation),
184
193
  tts: this.serialize(chunk.tts),
185
- audioChunk: this.serialize(chunk.audioChunk)
194
+ streamingChunk: this.serialize(chunk.streamingChunk),
195
+ isComplete: String(chunk.isComplete),
196
+ isSaved: String(chunk.isSaved)
186
197
  });
187
198
  p.expire(key, EXPIRATION);
188
199
  await p.exec();
189
200
  return chunk;
190
201
  }
191
- async updateAudioFilePath(roomId, chunkNumber, audioFilePath) {
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) {
202
+ async updateFinalTranscription(roomId, n, transcription) {
207
203
  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) {
237
- return this.withChunk(roomId, n, (c) => {
238
- Object.values(c.translation).forEach((t) => (t.status = 'DISCARDED'));
239
- Object.values(c.tts).forEach((t) => (t.status = 'DISCARDED'));
204
+ c.finalTranscription = transcription;
240
205
  });
241
206
  }
242
207
  async discardLanguage(roomId, n, lang) {
@@ -302,6 +267,47 @@ class MulingstreamChunkManager {
302
267
  const c = await this.getMulingstreamChunkById(roomId, n);
303
268
  return !!c && Object.values(c.translation).every((t) => t.status !== 'INIT');
304
269
  }
270
+ /**
271
+ * Check if all TTS for all target languages are done (READY, USED, or DISCARDED - not INIT)
272
+ */
273
+ async isChunkComplete(roomId, n) {
274
+ const c = await this.getMulingstreamChunkById(roomId, n);
275
+ if (!c)
276
+ return false;
277
+ return Object.values(c.tts).every((t) => t.status !== 'INIT');
278
+ }
279
+ /**
280
+ * Mark a chunk as complete (all TTS finished for all target languages)
281
+ */
282
+ async markChunkComplete(roomId, n) {
283
+ return this.withChunk(roomId, n, (c) => {
284
+ c.isComplete = true;
285
+ });
286
+ }
287
+ /**
288
+ * Mark a chunk as saved to database
289
+ */
290
+ async markChunkSaved(roomId, n) {
291
+ return this.withChunk(roomId, n, (c) => {
292
+ c.isSaved = true;
293
+ });
294
+ }
295
+ /**
296
+ * Get all complete but unsaved chunks for a room
297
+ */
298
+ async getCompleteUnsavedChunks(roomId) {
299
+ var _a;
300
+ const chunks = (_a = (await this.getMulingstreamChunksByRoom(roomId))) !== null && _a !== void 0 ? _a : [];
301
+ return chunks.filter((c) => c.isComplete && !c.isSaved);
302
+ }
303
+ /**
304
+ * Mark multiple chunks as saved in batch
305
+ */
306
+ async markChunksSaved(roomId, chunkNumbers) {
307
+ for (const n of chunkNumbers) {
308
+ await this.markChunkSaved(roomId, n);
309
+ }
310
+ }
305
311
  /**
306
312
  * @deprecated This function is replaced by ChunkSequencer (5-sequencer-handler.ts)
307
313
  *
@@ -348,9 +354,8 @@ class MulingstreamChunkManager {
348
354
  const now = Date.now();
349
355
  for (let i = lastDone + 1; i < window.length; i++) {
350
356
  const entry = window[i].tts[lang];
351
- const audio = window[i].audioChunk;
352
357
  if (entry.status === 'INIT') {
353
- if (now - window[i].createdAt > this.getTimeout(audio.start, audio.end))
358
+ if (now - window[i].createdAt > this.getTimeout())
354
359
  entry.status = 'DISCARDED';
355
360
  else
356
361
  blocked = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mulingai-npm/redis",
3
- "version": "3.29.1",
3
+ "version": "3.29.3",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "repository": {