@kuralle-syrinx/gemini 3.1.0 → 4.0.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/package.json +2 -2
- package/src/index.ts +30 -11
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kuralle-syrinx/gemini",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"types": "./src/index.ts",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@google/genai": "^1.0.0",
|
|
11
|
-
"@kuralle-syrinx/core": "
|
|
11
|
+
"@kuralle-syrinx/core": "4.0.0"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"typescript": "^5.7.0",
|
package/src/index.ts
CHANGED
|
@@ -61,7 +61,10 @@ export class GeminiTTSPlugin implements VoicePlugin {
|
|
|
61
61
|
private voiceName: string = DEFAULT_VOICE;
|
|
62
62
|
private instruction = "";
|
|
63
63
|
private timeoutMs = 45_000;
|
|
64
|
-
|
|
64
|
+
// One controller per in-flight turn. A single shared field let a second turn's
|
|
65
|
+
// synthesize() overwrite the first's controller, so a barge-in on turn N aborted
|
|
66
|
+
// only turn N+1 and turn N's stale audio could still stream after the interrupt.
|
|
67
|
+
private readonly abortControllers = new Map<string, AbortController>();
|
|
65
68
|
private textByContextId = new Map<string, string>();
|
|
66
69
|
private retryConfig: RetryConfig = readRetryConfig({});
|
|
67
70
|
private disposers: Array<() => void> = [];
|
|
@@ -98,10 +101,14 @@ export class GeminiTTSPlugin implements VoicePlugin {
|
|
|
98
101
|
await this.synthesize(text, donePkt.contextId);
|
|
99
102
|
}),
|
|
100
103
|
|
|
101
|
-
// Listen for TTS interrupts
|
|
102
|
-
bus.on("interrupt.tts", () => {
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
// Listen for TTS interrupts — abort only the interrupted turn's synthesis.
|
|
105
|
+
bus.on("interrupt.tts", (pkt) => {
|
|
106
|
+
const ctxId = (pkt as { contextId: string }).contextId;
|
|
107
|
+
const controller = this.abortControllers.get(ctxId);
|
|
108
|
+
if (controller) {
|
|
109
|
+
controller.abort();
|
|
110
|
+
this.abortControllers.delete(ctxId);
|
|
111
|
+
}
|
|
105
112
|
}),
|
|
106
113
|
);
|
|
107
114
|
}
|
|
@@ -109,9 +116,21 @@ export class GeminiTTSPlugin implements VoicePlugin {
|
|
|
109
116
|
private async synthesize(text: string, contextId: string): Promise<void> {
|
|
110
117
|
if (!this.bus) return;
|
|
111
118
|
|
|
112
|
-
|
|
113
|
-
|
|
119
|
+
const controller = new AbortController();
|
|
120
|
+
this.abortControllers.set(contextId, controller);
|
|
121
|
+
const signal = controller.signal;
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
await this.synthesizeWithRetry(text, contextId, signal);
|
|
125
|
+
} finally {
|
|
126
|
+
// Only clear if still ours — an interrupt may have already removed it.
|
|
127
|
+
if (this.abortControllers.get(contextId) === controller) {
|
|
128
|
+
this.abortControllers.delete(contextId);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
114
132
|
|
|
133
|
+
private async synthesizeWithRetry(text: string, contextId: string, signal: AbortSignal): Promise<void> {
|
|
115
134
|
for (let attempt = 1; attempt <= this.retryConfig.maxAttempts; attempt += 1) {
|
|
116
135
|
try {
|
|
117
136
|
const audioChunks = await this.synthesizeOnce(text, contextId, signal);
|
|
@@ -216,14 +235,14 @@ export class GeminiTTSPlugin implements VoicePlugin {
|
|
|
216
235
|
return audioChunks;
|
|
217
236
|
}
|
|
218
237
|
|
|
219
|
-
/** Flush/cancel
|
|
238
|
+
/** Flush/cancel all in-flight synthesis (called on interrupt/shutdown). */
|
|
220
239
|
flush(): void {
|
|
221
|
-
this.
|
|
222
|
-
this.
|
|
240
|
+
for (const controller of this.abortControllers.values()) controller.abort();
|
|
241
|
+
this.abortControllers.clear();
|
|
223
242
|
}
|
|
224
243
|
|
|
225
244
|
async close(): Promise<void> {
|
|
226
|
-
this.
|
|
245
|
+
this.flush();
|
|
227
246
|
for (const dispose of this.disposers.splice(0)) dispose();
|
|
228
247
|
this.textByContextId.clear();
|
|
229
248
|
this.bus = null;
|