@mulingai-npm/redis 3.40.24 → 3.40.26
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.
|
@@ -65,6 +65,15 @@ export type MulingstreamChunkData = {
|
|
|
65
65
|
transcriptionSource: 'stt' | 'llm';
|
|
66
66
|
routeUsed?: 'LLM' | 'AZURE' | 'AZURE_FALLBACK';
|
|
67
67
|
sttHistory?: SttHistoryEntry[];
|
|
68
|
+
/** Lifecycle status w.r.t. the pending-fragment buffer.
|
|
69
|
+
* - 'EMITTED' (default): chunk produced sanitized output and translations.
|
|
70
|
+
* - 'DEFERRED': chunk held back; its content lives in the pending buffer until consumed.
|
|
71
|
+
* - 'USED': chunk's content was rolled into a later chunk's emission. */
|
|
72
|
+
bufferStatus?: 'EMITTED' | 'DEFERRED' | 'USED';
|
|
73
|
+
/** Trailing fragment buffered for the next chunk (set on DEFERRED rows). */
|
|
74
|
+
pendingText?: string;
|
|
75
|
+
/** Chunks whose deferred content was absorbed into this chunk's emission. */
|
|
76
|
+
consumedChunkNumbers?: number[];
|
|
68
77
|
};
|
|
69
78
|
export declare class MulingstreamChunkManager {
|
|
70
79
|
private redisClient;
|
|
@@ -146,6 +155,16 @@ export declare class MulingstreamChunkManager {
|
|
|
146
155
|
isEmitted?: boolean;
|
|
147
156
|
duration?: number;
|
|
148
157
|
}): Promise<MulingstreamChunkData | null>;
|
|
158
|
+
/**
|
|
159
|
+
* Update SmartTranslate buffering metadata on a chunk (Section 22).
|
|
160
|
+
* Writes `bufferStatus`, `pendingText`, and `consumedChunkNumbers` so the chunk row
|
|
161
|
+
* persisted at the end of the pipeline reflects whether it was DEFERRED / USED / EMITTED.
|
|
162
|
+
*/
|
|
163
|
+
setBufferStatus(roomId: string, n: number, opt: {
|
|
164
|
+
bufferStatus?: 'EMITTED' | 'DEFERRED' | 'USED';
|
|
165
|
+
pendingText?: string;
|
|
166
|
+
consumedChunkNumbers?: number[];
|
|
167
|
+
}): Promise<MulingstreamChunkData | null>;
|
|
149
168
|
areTranslationsProcessed(roomId: string, n: number): Promise<boolean>;
|
|
150
169
|
/**
|
|
151
170
|
* Check if all TTS for all target languages are done (READY, USED, or DISCARDED - not INIT)
|
|
@@ -342,6 +342,21 @@ class MulingstreamChunkManager {
|
|
|
342
342
|
e.duration = opt.duration;
|
|
343
343
|
});
|
|
344
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* Update SmartTranslate buffering metadata on a chunk (Section 22).
|
|
347
|
+
* Writes `bufferStatus`, `pendingText`, and `consumedChunkNumbers` so the chunk row
|
|
348
|
+
* persisted at the end of the pipeline reflects whether it was DEFERRED / USED / EMITTED.
|
|
349
|
+
*/
|
|
350
|
+
async setBufferStatus(roomId, n, opt) {
|
|
351
|
+
return this.withChunk(roomId, n, (c) => {
|
|
352
|
+
if (opt.bufferStatus !== undefined)
|
|
353
|
+
c.bufferStatus = opt.bufferStatus;
|
|
354
|
+
if (opt.pendingText !== undefined)
|
|
355
|
+
c.pendingText = opt.pendingText;
|
|
356
|
+
if (opt.consumedChunkNumbers !== undefined)
|
|
357
|
+
c.consumedChunkNumbers = opt.consumedChunkNumbers;
|
|
358
|
+
});
|
|
359
|
+
}
|
|
345
360
|
async areTranslationsProcessed(roomId, n) {
|
|
346
361
|
const c = await this.getMulingstreamChunkById(roomId, n);
|
|
347
362
|
return !!c && Object.values(c.translation).every((t) => t.status !== 'INIT');
|
|
@@ -15,10 +15,22 @@ export interface ContextChunk {
|
|
|
15
15
|
san: boolean;
|
|
16
16
|
ts: number;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Pending buffer entry for the SmartTranslate buffering contract (Section 22).
|
|
20
|
+
* Stores the trailing fragment from the most-recent LLM response so it can be
|
|
21
|
+
* prepended to the next chunk's input.
|
|
22
|
+
*/
|
|
23
|
+
export interface PendingBuffer {
|
|
24
|
+
text: string;
|
|
25
|
+
seqs: number[];
|
|
26
|
+
consecutiveDeferred: number;
|
|
27
|
+
ts: number;
|
|
28
|
+
}
|
|
18
29
|
export declare class SmartTranslateContextManager {
|
|
19
30
|
private redisClient;
|
|
20
31
|
constructor(redisClient: RedisClient);
|
|
21
32
|
private contextKey;
|
|
33
|
+
private pendingKey;
|
|
22
34
|
/**
|
|
23
35
|
* Get the last N context chunks for a session (best-effort).
|
|
24
36
|
* Returns whatever is available RIGHT NOW — doesn't wait for pending LLM results.
|
|
@@ -47,4 +59,20 @@ export declare class SmartTranslateContextManager {
|
|
|
47
59
|
* Clean up context for a session (called when streaming ends).
|
|
48
60
|
*/
|
|
49
61
|
cleanupSession(sessionId: string): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Read the current pending fragment for a session (best-effort).
|
|
64
|
+
* Returns null if no buffer is set or Redis is unavailable.
|
|
65
|
+
*/
|
|
66
|
+
getPending(sessionId: string): Promise<PendingBuffer | null>;
|
|
67
|
+
/**
|
|
68
|
+
* Overwrite the pending fragment for a session.
|
|
69
|
+
* Pass an empty `text` to clear the buffer (a no-op write that effectively
|
|
70
|
+
* resets the deferred-chunk tracking).
|
|
71
|
+
*/
|
|
72
|
+
setPending(sessionId: string, buffer: PendingBuffer): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Clear the pending fragment unconditionally. Returns the cleared buffer (if any)
|
|
75
|
+
* so callers performing a session-end flush can emit the dropped fragment.
|
|
76
|
+
*/
|
|
77
|
+
clearPending(sessionId: string): Promise<PendingBuffer | null>;
|
|
50
78
|
}
|
|
@@ -20,6 +20,9 @@ class SmartTranslateContextManager {
|
|
|
20
20
|
contextKey(sessionId) {
|
|
21
21
|
return `smarttranslate:context:${sessionId}`;
|
|
22
22
|
}
|
|
23
|
+
pendingKey(sessionId) {
|
|
24
|
+
return `smarttranslate:pending:${sessionId}`;
|
|
25
|
+
}
|
|
23
26
|
/**
|
|
24
27
|
* Get the last N context chunks for a session (best-effort).
|
|
25
28
|
* Returns whatever is available RIGHT NOW — doesn't wait for pending LLM results.
|
|
@@ -122,10 +125,70 @@ class SmartTranslateContextManager {
|
|
|
122
125
|
async cleanupSession(sessionId) {
|
|
123
126
|
try {
|
|
124
127
|
await this.redisClient.del(this.contextKey(sessionId));
|
|
128
|
+
await this.redisClient.del(this.pendingKey(sessionId));
|
|
125
129
|
}
|
|
126
130
|
catch {
|
|
127
131
|
// Cleanup failure is non-critical
|
|
128
132
|
}
|
|
129
133
|
}
|
|
134
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
135
|
+
// Pending Buffer (Section 22 — Incomplete Chunk Merging / Buffering)
|
|
136
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
137
|
+
/**
|
|
138
|
+
* Read the current pending fragment for a session (best-effort).
|
|
139
|
+
* Returns null if no buffer is set or Redis is unavailable.
|
|
140
|
+
*/
|
|
141
|
+
async getPending(sessionId) {
|
|
142
|
+
try {
|
|
143
|
+
const raw = await this.redisClient.get(this.pendingKey(sessionId));
|
|
144
|
+
if (!raw)
|
|
145
|
+
return null;
|
|
146
|
+
try {
|
|
147
|
+
return JSON.parse(raw);
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
console.warn(`[SmartTranslateContextManager] getPending FAILED for ${sessionId}: ${err}`);
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Overwrite the pending fragment for a session.
|
|
160
|
+
* Pass an empty `text` to clear the buffer (a no-op write that effectively
|
|
161
|
+
* resets the deferred-chunk tracking).
|
|
162
|
+
*/
|
|
163
|
+
async setPending(sessionId, buffer) {
|
|
164
|
+
try {
|
|
165
|
+
const key = this.pendingKey(sessionId);
|
|
166
|
+
if (!buffer.text) {
|
|
167
|
+
await this.redisClient.del(key);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const pipe = this.redisClient.pipeline();
|
|
171
|
+
pipe.set(key, JSON.stringify(buffer));
|
|
172
|
+
pipe.expire(key, CONTEXT_TTL);
|
|
173
|
+
await pipe.exec();
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
console.warn(`[SmartTranslateContextManager] setPending FAILED for ${sessionId}: ${err}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Clear the pending fragment unconditionally. Returns the cleared buffer (if any)
|
|
181
|
+
* so callers performing a session-end flush can emit the dropped fragment.
|
|
182
|
+
*/
|
|
183
|
+
async clearPending(sessionId) {
|
|
184
|
+
const current = await this.getPending(sessionId);
|
|
185
|
+
try {
|
|
186
|
+
await this.redisClient.del(this.pendingKey(sessionId));
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
console.warn(`[SmartTranslateContextManager] clearPending FAILED for ${sessionId}: ${err}`);
|
|
190
|
+
}
|
|
191
|
+
return current;
|
|
192
|
+
}
|
|
130
193
|
}
|
|
131
194
|
exports.SmartTranslateContextManager = SmartTranslateContextManager;
|