dsh-live-voice 0.6.0 → 0.6.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.
- package/README.md +1 -1
- package/lib/asr/asr.worker.js +1 -1
- package/lib/asr/manifest.json +1 -1
- package/lib/client.js +483 -95
- package/lib/server.js +47 -7
- package/lib/vad/manifest.json +2 -2
- package/lib/vad/vad.worker.js +1 -1
- package/package.json +7 -5
- package/src/app/client/apply.tsx +16 -2
- package/src/app/client/i18n/catalogs/base.ts +6 -1
- package/src/app/client/i18n/catalogs/en.ts +10 -1
- package/src/app/client/i18n/catalogs/es.ts +10 -1
- package/src/app/client/i18n/catalogs/fr.ts +10 -1
- package/src/app/client/i18n/catalogs/hi.ts +10 -1
- package/src/app/client/i18n/catalogs/pt-BR.ts +10 -1
- package/src/app/client/i18n/catalogs/zh.ts +10 -1
- package/src/app/server/apply.ts +19 -0
- package/src/modules/conversation/ARCHITECTURE.md +1 -1
- package/src/modules/conversation/models/meeting.ts +17 -4
- package/src/modules/core/coordinator.ts +66 -11
- package/src/modules/core/qwen/QwenHttpHost.ts +5 -1
- package/src/modules/core/settings.ts +17 -7
- package/src/modules/recognition/ARCHITECTURE.md +3 -1
- package/src/modules/recognition/engines/whisper/WhisperRecognitionEngine.ts +9 -1
- package/src/modules/recognition/engines/whisper/whisperRecognitionHost.ts +4 -1
- package/src/modules/recognition/local/BrowserLocalRecognitionEngine.ts +4 -1
- package/src/modules/recognition/transcriptionContext.ts +28 -0
- package/src/modules/recording/ARCHITECTURE.md +8 -2
- package/src/modules/recording/PinDetector.ts +6 -0
- package/src/modules/recording/RecordingRecognition.ts +198 -57
- package/src/modules/recording/codec.ts +4 -0
- package/src/modules/recording/repository.ts +137 -5
- package/src/modules/recording/storage.ts +21 -1
- package/src/modules/recording/types.ts +19 -0
- package/src/modules/settings/ARCHITECTURE.md +1 -1
- package/src/modules/settings/sections/conversation/ConversationDelaySettings.tsx +9 -7
- package/src/modules/settings/sections/recognition/RecognitionEngineSettings.tsx +17 -9
- package/src/modules/settings/services/pluginUpdate.ts +10 -1
package/lib/client.js
CHANGED
|
@@ -152,7 +152,7 @@ var SessionSpeechMemory = class {
|
|
|
152
152
|
};
|
|
153
153
|
|
|
154
154
|
// src/modules/recognition/vad/assets.ts
|
|
155
|
-
var sileroVadAssetBase = () => "/api/dsh-live-voice/vad/" + encodeURIComponent(true ? "0.6.
|
|
155
|
+
var sileroVadAssetBase = () => "/api/dsh-live-voice/vad/" + encodeURIComponent(true ? "0.6.2" : "test") + "/";
|
|
156
156
|
async function sileroVadAvailable(fetchImpl = globalThis.fetch) {
|
|
157
157
|
try {
|
|
158
158
|
const response = await fetchImpl(sileroVadAssetBase() + "availability", {
|
|
@@ -615,6 +615,7 @@ var RecordingRepositoryError = class extends Error {
|
|
|
615
615
|
}
|
|
616
616
|
code;
|
|
617
617
|
};
|
|
618
|
+
var audioPageBytes = (page) => (page.decoderConfig?.description?.byteLength ?? 0) + page.packets.reduce((total, packet) => total + packet.data.byteLength, 0);
|
|
618
619
|
var globals = () => globalThis;
|
|
619
620
|
function unsupported(cause) {
|
|
620
621
|
return new RecordingRepositoryError(
|
|
@@ -919,6 +920,19 @@ async function openRecordingStore() {
|
|
|
919
920
|
}
|
|
920
921
|
};
|
|
921
922
|
}),
|
|
923
|
+
audioBytes: (id) => transact("readonly", (tx, result) => {
|
|
924
|
+
let bytes = 0;
|
|
925
|
+
const request = tx.objectStore("pages").index("recordingId").openCursor(IDBKeyRange.only(id));
|
|
926
|
+
request.onsuccess = () => {
|
|
927
|
+
const cursor = request.result;
|
|
928
|
+
if (!cursor) {
|
|
929
|
+
result(bytes);
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
bytes += audioPageBytes(cursor.value);
|
|
933
|
+
cursor.continue();
|
|
934
|
+
};
|
|
935
|
+
}),
|
|
922
936
|
nextPage: (id, start, end) => transact("readonly", (tx, result) => {
|
|
923
937
|
if (end <= start) {
|
|
924
938
|
result(null);
|
|
@@ -956,6 +970,7 @@ function createMemoryStore(maxBytes = 32 * 1024 * 1024) {
|
|
|
956
970
|
let used = 0;
|
|
957
971
|
const size2 = (page) => 256 + (page.decoderConfig?.description?.byteLength ?? 0) + page.packets.reduce((total, packet) => total + packet.data.byteLength + 64, 0);
|
|
958
972
|
return {
|
|
973
|
+
audioBytes: async (id) => (pages.get(id) ?? []).reduce((sum, page) => sum + audioPageBytes(page), 0),
|
|
959
974
|
list: async () => structuredClone([...sessions.values()]),
|
|
960
975
|
get: async (id) => structuredClone(sessions.get(id) ?? null),
|
|
961
976
|
create: async (session) => {
|
|
@@ -1204,6 +1219,17 @@ function createRecordingRepository() {
|
|
|
1204
1219
|
}
|
|
1205
1220
|
return locate(id);
|
|
1206
1221
|
}
|
|
1222
|
+
const measured = /* @__PURE__ */ new Map();
|
|
1223
|
+
async function withAudioBytes(session) {
|
|
1224
|
+
if (session.audioBytes !== void 0) return session;
|
|
1225
|
+
let cached = measured.get(session.id);
|
|
1226
|
+
if (!cached || cached.samples !== session.savedSamples) {
|
|
1227
|
+
const { store } = await locate(session.id);
|
|
1228
|
+
cached = { samples: session.savedSamples, bytes: await store.audioBytes(session.id) };
|
|
1229
|
+
measured.set(session.id, cached);
|
|
1230
|
+
}
|
|
1231
|
+
return { ...session, audioBytes: cached.bytes };
|
|
1232
|
+
}
|
|
1207
1233
|
const repository = {
|
|
1208
1234
|
version: 1,
|
|
1209
1235
|
segments: recordingSegments,
|
|
@@ -1214,14 +1240,21 @@ function createRecordingRepository() {
|
|
|
1214
1240
|
},
|
|
1215
1241
|
async list() {
|
|
1216
1242
|
check2();
|
|
1217
|
-
|
|
1218
|
-
|
|
1243
|
+
const sessions = await all();
|
|
1244
|
+
const ids = new Set(sessions.map((session) => session.id));
|
|
1245
|
+
for (const id of measured.keys()) if (!ids.has(id)) measured.delete(id);
|
|
1246
|
+
return Promise.all(
|
|
1247
|
+
sessions.map(
|
|
1248
|
+
(session) => withAudioBytes(writers.get(session.id)?.writer.session ?? cloneSession(session))
|
|
1249
|
+
)
|
|
1219
1250
|
);
|
|
1220
1251
|
},
|
|
1221
1252
|
async get(id) {
|
|
1222
1253
|
check2();
|
|
1223
1254
|
try {
|
|
1224
|
-
return
|
|
1255
|
+
return await withAudioBytes(
|
|
1256
|
+
writers.get(id)?.writer.session ?? cloneSession((await locate(id)).session)
|
|
1257
|
+
);
|
|
1225
1258
|
} catch (error) {
|
|
1226
1259
|
if (error instanceof RecordingRepositoryError && error.code === "not-found") return null;
|
|
1227
1260
|
throw error;
|
|
@@ -1326,6 +1359,66 @@ function createRecordingRepository() {
|
|
|
1326
1359
|
emit();
|
|
1327
1360
|
});
|
|
1328
1361
|
},
|
|
1362
|
+
async setName(id, value) {
|
|
1363
|
+
check2();
|
|
1364
|
+
if (typeof value !== "string" || value.trim().length > 120 || /[\u0000-\u001f\u007f-\u009f\u2028\u2029]/u.test(value))
|
|
1365
|
+
throw new RecordingRepositoryError(
|
|
1366
|
+
"invalid-range",
|
|
1367
|
+
"Recording names must be a single line of at most 120 characters."
|
|
1368
|
+
);
|
|
1369
|
+
const name = value.trim();
|
|
1370
|
+
const active = writers.get(id);
|
|
1371
|
+
if (active) return active.setName(name);
|
|
1372
|
+
await serial(async () => {
|
|
1373
|
+
const { store } = await locate(id);
|
|
1374
|
+
const release = store === memory ? () => {
|
|
1375
|
+
} : await holdLock(id);
|
|
1376
|
+
try {
|
|
1377
|
+
await store.update(id, (current) => {
|
|
1378
|
+
if (!current.starred)
|
|
1379
|
+
throw new RecordingRepositoryError(
|
|
1380
|
+
"invalid-range",
|
|
1381
|
+
"Star a recording before naming it."
|
|
1382
|
+
);
|
|
1383
|
+
return { ...current, name };
|
|
1384
|
+
});
|
|
1385
|
+
} finally {
|
|
1386
|
+
release();
|
|
1387
|
+
}
|
|
1388
|
+
emit();
|
|
1389
|
+
});
|
|
1390
|
+
},
|
|
1391
|
+
timeline() {
|
|
1392
|
+
const targets = [...writers.entries()];
|
|
1393
|
+
return async (kind) => {
|
|
1394
|
+
const occurredAt = now();
|
|
1395
|
+
await Promise.all(
|
|
1396
|
+
targets.map(async ([id, active]) => {
|
|
1397
|
+
const value = {
|
|
1398
|
+
id: crypto.randomUUID(),
|
|
1399
|
+
kind,
|
|
1400
|
+
occurredAt,
|
|
1401
|
+
atSample: active.writer.session.capturedSamples
|
|
1402
|
+
};
|
|
1403
|
+
if (writers.get(id) === active) return active.event(value);
|
|
1404
|
+
await serial(async () => {
|
|
1405
|
+
const { store } = await locate(id);
|
|
1406
|
+
const release = store === memory ? () => {
|
|
1407
|
+
} : await holdLock(id);
|
|
1408
|
+
try {
|
|
1409
|
+
await store.update(id, (current) => ({
|
|
1410
|
+
...current,
|
|
1411
|
+
events: [...current.events ?? [], value]
|
|
1412
|
+
}));
|
|
1413
|
+
} finally {
|
|
1414
|
+
release();
|
|
1415
|
+
}
|
|
1416
|
+
emit();
|
|
1417
|
+
});
|
|
1418
|
+
})
|
|
1419
|
+
);
|
|
1420
|
+
};
|
|
1421
|
+
},
|
|
1329
1422
|
async begin(input) {
|
|
1330
1423
|
check2();
|
|
1331
1424
|
return serial(async () => {
|
|
@@ -1363,12 +1456,16 @@ function createRecordingRepository() {
|
|
|
1363
1456
|
state: "recording",
|
|
1364
1457
|
capturedSamples: 0,
|
|
1365
1458
|
savedSamples: 0,
|
|
1459
|
+
audioBytes: 0,
|
|
1366
1460
|
persistent,
|
|
1367
1461
|
codec: "opus",
|
|
1368
1462
|
storage: policy.enabled ? "indexeddb" : "memory",
|
|
1369
1463
|
configuration: input.configuration,
|
|
1370
1464
|
pins: [],
|
|
1371
1465
|
attempts: [],
|
|
1466
|
+
events: [
|
|
1467
|
+
{ id: crypto.randomUUID(), kind: "capture-start", occurredAt: now(), atSample: 0 }
|
|
1468
|
+
],
|
|
1372
1469
|
keepPrevious: Math.max(1, Math.floor(policy.keepPrevious))
|
|
1373
1470
|
};
|
|
1374
1471
|
try {
|
|
@@ -1422,7 +1519,14 @@ function createRecordingRepository() {
|
|
|
1422
1519
|
sampleRate,
|
|
1423
1520
|
...encoded
|
|
1424
1521
|
};
|
|
1425
|
-
await update(
|
|
1522
|
+
await update(
|
|
1523
|
+
(current) => ({
|
|
1524
|
+
...current,
|
|
1525
|
+
savedSamples: page.startSample + count,
|
|
1526
|
+
audioBytes: (current.audioBytes ?? 0) + audioPageBytes(page)
|
|
1527
|
+
}),
|
|
1528
|
+
page
|
|
1529
|
+
);
|
|
1426
1530
|
pendingCount = 0;
|
|
1427
1531
|
index++;
|
|
1428
1532
|
};
|
|
@@ -1553,7 +1657,16 @@ function createRecordingRepository() {
|
|
|
1553
1657
|
...current,
|
|
1554
1658
|
state: fatal ? "failed" : state,
|
|
1555
1659
|
error: fatal?.code ?? error,
|
|
1556
|
-
endedAt: now()
|
|
1660
|
+
endedAt: now(),
|
|
1661
|
+
events: [
|
|
1662
|
+
...current.events ?? [],
|
|
1663
|
+
{
|
|
1664
|
+
id: crypto.randomUUID(),
|
|
1665
|
+
kind: "capture-end",
|
|
1666
|
+
occurredAt: now(),
|
|
1667
|
+
atSample: current.capturedSamples
|
|
1668
|
+
}
|
|
1669
|
+
]
|
|
1557
1670
|
}));
|
|
1558
1671
|
} finally {
|
|
1559
1672
|
pending = new Float32Array(0);
|
|
@@ -1572,6 +1685,25 @@ function createRecordingRepository() {
|
|
|
1572
1685
|
setStarred: (value) => enqueue(async () => {
|
|
1573
1686
|
await update((current) => ({ ...current, starred: value }));
|
|
1574
1687
|
}),
|
|
1688
|
+
setName: (name) => enqueue(async () => {
|
|
1689
|
+
await update((current) => {
|
|
1690
|
+
if (!current.starred)
|
|
1691
|
+
throw new RecordingRepositoryError(
|
|
1692
|
+
"invalid-range",
|
|
1693
|
+
"Star a recording before naming it."
|
|
1694
|
+
);
|
|
1695
|
+
return { ...current, name };
|
|
1696
|
+
});
|
|
1697
|
+
}),
|
|
1698
|
+
event: (value) => enqueue(async () => {
|
|
1699
|
+
await update((current) => ({
|
|
1700
|
+
...current,
|
|
1701
|
+
events: [
|
|
1702
|
+
...current.events ?? [],
|
|
1703
|
+
{ ...value, atSample: current.capturedSamples }
|
|
1704
|
+
]
|
|
1705
|
+
}));
|
|
1706
|
+
}),
|
|
1575
1707
|
idle: async () => {
|
|
1576
1708
|
await tail;
|
|
1577
1709
|
}
|
|
@@ -1734,8 +1866,10 @@ var en = {
|
|
|
1734
1866
|
"dsh-live-voice.commons.update.cancel": "Cancel",
|
|
1735
1867
|
"dsh-live-voice.commons.update.confirm": "Install Live Voice {version} in this DSH profile? Download requires network access. Restart DSH afterward when requested; nothing restarts automatically.",
|
|
1736
1868
|
"dsh-live-voice.commons.update.current": "This version is already installed. Restart DSH to use it.",
|
|
1869
|
+
"dsh-live-voice.commons.update.delayed": "The requested version is not available to this profile yet. It may still be publishing or be delayed by pnpm minimumReleaseAge. Wait, then check the DSH plugin manager; no security policy was bypassed.",
|
|
1737
1870
|
"dsh-live-voice.commons.update.done": "Live Voice {version} installed. Reload the page when ready.",
|
|
1738
1871
|
"dsh-live-voice.commons.update.failed": "Update failed. The release may not be published on npm yet. Check the DSH plugin manager.",
|
|
1872
|
+
"dsh-live-voice.commons.update.incompatible": "This release is incompatible with the running DSH version. Check the DSH plugin manager and use a compatible DSH/plugin version; no compatibility exemption was granted.",
|
|
1739
1873
|
"dsh-live-voice.commons.update.installing": "Updating\u2026",
|
|
1740
1874
|
"dsh-live-voice.commons.update.label": "Update available",
|
|
1741
1875
|
"dsh-live-voice.commons.update.link": "Update available: {version}. Open release",
|
|
@@ -1805,6 +1939,9 @@ var en = {
|
|
|
1805
1939
|
"dsh-live-voice.recognition.commands.send": "Send to running agent",
|
|
1806
1940
|
"dsh-live-voice.recognition.commands.stopSpeech": "Stop assistant speech",
|
|
1807
1941
|
"dsh-live-voice.recognition.commands.title": "Voice commands",
|
|
1942
|
+
"dsh-live-voice.recognition.context.hint": "Optional background and vocabulary sent as prompt to the selected HTTP speech service, together with each audio segment. Saved on blur. Support depends on the server; this does not restrict languages or guarantee instruction following. Leave empty to send no context.",
|
|
1943
|
+
"dsh-live-voice.recognition.context.label": "Transcription context",
|
|
1944
|
+
"dsh-live-voice.recognition.context.placeholder": "Native Brazilian Portuguese speaker who frequently uses technical terms in English.",
|
|
1808
1945
|
"dsh-live-voice.recognition.dictation.cancel": "Cancel dictation",
|
|
1809
1946
|
"dsh-live-voice.recognition.engine.label": "Recognition engine",
|
|
1810
1947
|
"dsh-live-voice.recognition.headphoneMode.help": "The microphone stays open during playback. Recognized non-echo speech can pause playback after the configured delay; it resumes after microphone and shared meeting audio stay silent for that delay. Manual Pause requires Resume.",
|
|
@@ -2077,7 +2214,7 @@ var en = {
|
|
|
2077
2214
|
"dsh-live-voice.speak.rate.label": "Speech rate",
|
|
2078
2215
|
"dsh-live-voice.speak.responseDelay.help": "After you stop speaking, automatic assistant playback waits for this much continuous silence. Speaking again restarts the wait.",
|
|
2079
2216
|
"dsh-live-voice.speak.responseDelay.label": "Assistant response delay",
|
|
2080
|
-
"dsh-live-voice.speak.responseDelay.
|
|
2217
|
+
"dsh-live-voice.speak.responseDelay.oneAndHalf": "1.5 seconds",
|
|
2081
2218
|
"dsh-live-voice.speak.segmentGap.help": "Wait this many milliseconds between consecutive spoken segments. 400 ms is the default.",
|
|
2082
2219
|
"dsh-live-voice.speak.segmentGap.label": "Pause between speech segments",
|
|
2083
2220
|
"dsh-live-voice.speak.status.paused": "Speech paused",
|
|
@@ -2129,8 +2266,10 @@ var es = {
|
|
|
2129
2266
|
"dsh-live-voice.commons.update.cancel": "Cancelar",
|
|
2130
2267
|
"dsh-live-voice.commons.update.confirm": "\xBFInstalar Voz en vivo {version} en este perfil de DSH? La descarga requiere red. Reinicia DSH despu\xE9s si se solicita; nada se reinicia autom\xE1ticamente.",
|
|
2131
2268
|
"dsh-live-voice.commons.update.current": "Esta versi\xF3n ya est\xE1 instalada. Reinicia DSH para usarla.",
|
|
2269
|
+
"dsh-live-voice.commons.update.delayed": "La versi\xF3n solicitada a\xFAn no est\xE1 disponible para este perfil. Puede estar public\xE1ndose o esperando el minimumReleaseAge de pnpm. Espera y consulta el gestor de plugins de DSH; no se omiti\xF3 ninguna pol\xEDtica de seguridad.",
|
|
2132
2270
|
"dsh-live-voice.commons.update.done": "Voz en vivo {version} instalado. Recarga la p\xE1gina cuando quieras.",
|
|
2133
2271
|
"dsh-live-voice.commons.update.failed": "La actualizaci\xF3n fall\xF3. Puede que la versi\xF3n a\xFAn no est\xE9 en npm. Revisa el gestor de plugins de DSH.",
|
|
2272
|
+
"dsh-live-voice.commons.update.incompatible": "Esta versi\xF3n es incompatible con el DSH en ejecuci\xF3n. Consulta el gestor de plugins y usa versiones compatibles de DSH y del plugin; no se concedi\xF3 ninguna excepci\xF3n.",
|
|
2134
2273
|
"dsh-live-voice.commons.update.installing": "Actualizando\u2026",
|
|
2135
2274
|
"dsh-live-voice.commons.update.label": "Actualizaci\xF3n disponible",
|
|
2136
2275
|
"dsh-live-voice.commons.update.link": "Actualizaci\xF3n disponible: {version}. Abrir la p\xE1gina de esta versi\xF3n",
|
|
@@ -2200,6 +2339,9 @@ var es = {
|
|
|
2200
2339
|
"dsh-live-voice.recognition.commands.send": "Enviar al agente en ejecuci\xF3n",
|
|
2201
2340
|
"dsh-live-voice.recognition.commands.stopSpeech": "Detener la lectura en voz alta del asistente",
|
|
2202
2341
|
"dsh-live-voice.recognition.commands.title": "Comandos de voz",
|
|
2342
|
+
"dsh-live-voice.recognition.context.hint": "Contexto y vocabulario opcionales enviados como prompt al servicio HTTP de voz seleccionado con cada segmento de audio. Se guarda al salir del campo. La compatibilidad depende del servidor; no restringe idiomas ni garantiza el seguimiento de instrucciones. D\xE9jalo vac\xEDo para no enviar contexto.",
|
|
2343
|
+
"dsh-live-voice.recognition.context.label": "Contexto de transcripci\xF3n",
|
|
2344
|
+
"dsh-live-voice.recognition.context.placeholder": "Native Brazilian Portuguese speaker who frequently uses technical terms in English.",
|
|
2203
2345
|
"dsh-live-voice.recognition.dictation.cancel": "Cancelar dictado",
|
|
2204
2346
|
"dsh-live-voice.recognition.engine.label": "Motor de reconocimiento",
|
|
2205
2347
|
"dsh-live-voice.recognition.headphoneMode.help": "El micr\xF3fono sigue abierto durante la reproducci\xF3n. El habla reconocida sin eco puede pausarla tras el retraso configurado; se reanuda cuando el micr\xF3fono y el audio compartido guardan silencio durante ese intervalo. La pausa manual requiere Reanudar.",
|
|
@@ -2472,7 +2614,7 @@ var es = {
|
|
|
2472
2614
|
"dsh-live-voice.speak.rate.label": "Velocidad de habla",
|
|
2473
2615
|
"dsh-live-voice.speak.responseDelay.help": "Cuando dejas de hablar, la reproducci\xF3n autom\xE1tica del asistente espera este intervalo de silencio continuo. Si vuelves a hablar, la espera se reinicia.",
|
|
2474
2616
|
"dsh-live-voice.speak.responseDelay.label": "Demora de respuesta del asistente",
|
|
2475
|
-
"dsh-live-voice.speak.responseDelay.
|
|
2617
|
+
"dsh-live-voice.speak.responseDelay.oneAndHalf": "1,5 segundos",
|
|
2476
2618
|
"dsh-live-voice.speak.segmentGap.help": "Espera esta cantidad de milisegundos entre segmentos hablados consecutivos. El valor predeterminado es 400 ms.",
|
|
2477
2619
|
"dsh-live-voice.speak.segmentGap.label": "Pausa entre segmentos de voz",
|
|
2478
2620
|
"dsh-live-voice.speak.status.paused": "Lectura en pausa",
|
|
@@ -2524,8 +2666,10 @@ var fr = {
|
|
|
2524
2666
|
"dsh-live-voice.commons.update.cancel": "Annuler",
|
|
2525
2667
|
"dsh-live-voice.commons.update.confirm": "Installer Voix en direct {version} dans ce profil DSH ? Le t\xE9l\xE9chargement n\xE9cessite le r\xE9seau. Red\xE9marrez DSH ensuite si demand\xE9 ; rien ne red\xE9marre automatiquement.",
|
|
2526
2668
|
"dsh-live-voice.commons.update.current": "Cette version est d\xE9j\xE0 install\xE9e. Red\xE9marrez DSH pour l\u2019utiliser.",
|
|
2669
|
+
"dsh-live-voice.commons.update.delayed": "La version demand\xE9e n\u2019est pas encore disponible pour ce profil. La publication ou le minimumReleaseAge de pnpm peut la retarder. Patientez puis consultez le gestionnaire DSH ; aucune r\xE8gle de s\xE9curit\xE9 n\u2019a \xE9t\xE9 contourn\xE9e.",
|
|
2527
2670
|
"dsh-live-voice.commons.update.done": "Voix en direct {version} install\xE9. Rechargez la page quand vous le souhaitez.",
|
|
2528
2671
|
"dsh-live-voice.commons.update.failed": "\xC9chec de mise \xE0 jour. La version n\u2019est peut-\xEAtre pas encore sur npm. Consultez le gestionnaire de plugins DSH.",
|
|
2672
|
+
"dsh-live-voice.commons.update.incompatible": "Cette version est incompatible avec le DSH en cours. Consultez le gestionnaire de plugins et utilisez des versions compatibles de DSH et du plugin ; aucune d\xE9rogation n\u2019a \xE9t\xE9 accord\xE9e.",
|
|
2529
2673
|
"dsh-live-voice.commons.update.installing": "Mise \xE0 jour\u2026",
|
|
2530
2674
|
"dsh-live-voice.commons.update.label": "Mise \xE0 jour disponible",
|
|
2531
2675
|
"dsh-live-voice.commons.update.link": "Mise \xE0 jour disponible : {version}. Ouvrir la page de cette version",
|
|
@@ -2595,6 +2739,9 @@ var fr = {
|
|
|
2595
2739
|
"dsh-live-voice.recognition.commands.send": "Envoyer \xE0 l\u2019agent en cours d\u2019ex\xE9cution",
|
|
2596
2740
|
"dsh-live-voice.recognition.commands.stopSpeech": "Arr\xEAter la lecture vocale de l\u2019assistant",
|
|
2597
2741
|
"dsh-live-voice.recognition.commands.title": "Commandes vocales",
|
|
2742
|
+
"dsh-live-voice.recognition.context.hint": "Contexte et vocabulaire facultatifs envoy\xE9s comme prompt au service vocal HTTP s\xE9lectionn\xE9 avec chaque segment audio. Enregistr\xE9 \xE0 la sortie du champ. La prise en charge d\xE9pend du serveur ; ne limite pas les langues et ne garantit pas le suivi des consignes. Laissez vide pour ne pas envoyer de contexte.",
|
|
2743
|
+
"dsh-live-voice.recognition.context.label": "Contexte de transcription",
|
|
2744
|
+
"dsh-live-voice.recognition.context.placeholder": "Native Brazilian Portuguese speaker who frequently uses technical terms in English.",
|
|
2598
2745
|
"dsh-live-voice.recognition.dictation.cancel": "Annuler la dict\xE9e",
|
|
2599
2746
|
"dsh-live-voice.recognition.engine.label": "Moteur de reconnaissance",
|
|
2600
2747
|
"dsh-live-voice.recognition.headphoneMode.help": "Le microphone reste ouvert pendant la lecture. La parole reconnue sans \xE9cho peut la mettre en pause apr\xE8s le d\xE9lai configur\xE9 ; elle reprend lorsque le microphone et le son partag\xE9 restent silencieux pendant ce d\xE9lai. Une pause manuelle n\xE9cessite Reprendre.",
|
|
@@ -2867,7 +3014,7 @@ var fr = {
|
|
|
2867
3014
|
"dsh-live-voice.speak.rate.label": "D\xE9bit de parole",
|
|
2868
3015
|
"dsh-live-voice.speak.responseDelay.help": "Apr\xE8s que vous avez cess\xE9 de parler, la lecture automatique de l\u2019assistant attend cette dur\xE9e de silence continu. Si vous reparlez, l\u2019attente recommence.",
|
|
2869
3016
|
"dsh-live-voice.speak.responseDelay.label": "D\xE9lai de r\xE9ponse de l\u2019assistant",
|
|
2870
|
-
"dsh-live-voice.speak.responseDelay.
|
|
3017
|
+
"dsh-live-voice.speak.responseDelay.oneAndHalf": "1,5 seconde",
|
|
2871
3018
|
"dsh-live-voice.speak.segmentGap.help": "Attend ce nombre de millisecondes entre des segments vocaux cons\xE9cutifs. La valeur par d\xE9faut est 400 ms.",
|
|
2872
3019
|
"dsh-live-voice.speak.segmentGap.label": "Pause entre les segments vocaux",
|
|
2873
3020
|
"dsh-live-voice.speak.status.paused": "Lecture en pause",
|
|
@@ -2919,8 +3066,10 @@ var hi = {
|
|
|
2919
3066
|
"dsh-live-voice.commons.update.cancel": "\u0930\u0926\u094D\u0926 \u0915\u0930\u0947\u0902",
|
|
2920
3067
|
"dsh-live-voice.commons.update.confirm": "\u0907\u0938 DSH \u092A\u094D\u0930\u094B\u092B\u093C\u093E\u0907\u0932 \u092E\u0947\u0902 \u0932\u093E\u0907\u0935 \u0935\u0949\u0907\u0938 {version} \u0907\u0902\u0938\u094D\u091F\u0949\u0932 \u0915\u0930\u0947\u0902? \u0921\u093E\u0909\u0928\u0932\u094B\u0921 \u0915\u0947 \u0932\u093F\u090F \u0928\u0947\u091F\u0935\u0930\u094D\u0915 \u091A\u093E\u0939\u093F\u090F\u0964 \u0905\u0928\u0941\u0930\u094B\u0927 \u0939\u094B\u0928\u0947 \u092A\u0930 \u092C\u093E\u0926 \u092E\u0947\u0902 DSH \u092A\u0941\u0928\u0903 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902; \u0915\u0941\u091B \u092D\u0940 \u0905\u092A\u0928\u0947 \u0906\u092A \u092A\u0941\u0928\u0903 \u0936\u0941\u0930\u0942 \u0928\u0939\u0940\u0902 \u0939\u094B\u0917\u093E\u0964",
|
|
2921
3068
|
"dsh-live-voice.commons.update.current": "\u092F\u0939 \u0938\u0902\u0938\u094D\u0915\u0930\u0923 \u092A\u0939\u0932\u0947 \u0938\u0947 \u0907\u0902\u0938\u094D\u091F\u0949\u0932 \u0939\u0948\u0964 \u0909\u092A\u092F\u094B\u0917 \u0915\u0947 \u0932\u093F\u090F DSH \u092A\u0941\u0928\u0903 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902\u0964",
|
|
3069
|
+
"dsh-live-voice.commons.update.delayed": "\u092E\u093E\u0901\u0917\u093E \u0917\u092F\u093E \u0938\u0902\u0938\u094D\u0915\u0930\u0923 \u0905\u092D\u0940 \u0907\u0938 \u092A\u094D\u0930\u094B\u092B\u093C\u093E\u0907\u0932 \u0915\u0947 \u0932\u093F\u090F \u0909\u092A\u0932\u092C\u094D\u0927 \u0928\u0939\u0940\u0902 \u0939\u0948\u0964 \u092A\u094D\u0930\u0915\u093E\u0936\u0928 \u091C\u093E\u0930\u0940 \u0939\u094B \u0938\u0915\u0924\u093E \u0939\u0948 \u092F\u093E pnpm \u0915\u093E minimumReleaseAge \u0935\u093F\u0932\u0902\u092C \u0915\u0930 \u0938\u0915\u0924\u093E \u0939\u0948\u0964 \u092A\u094D\u0930\u0924\u0940\u0915\u094D\u0937\u093E \u0915\u0930\u0947\u0902, \u092B\u093F\u0930 DSH \u092A\u094D\u0932\u0917\u0907\u0928 \u092A\u094D\u0930\u092C\u0902\u0927\u0915 \u0926\u0947\u0916\u0947\u0902; \u0915\u094B\u0908 \u0938\u0941\u0930\u0915\u094D\u0937\u093E \u0928\u0940\u0924\u093F \u0928\u0939\u0940\u0902 \u092C\u0926\u0932\u0940 \u0917\u0908\u0964",
|
|
2922
3070
|
"dsh-live-voice.commons.update.done": "\u0932\u093E\u0907\u0935 \u0935\u0949\u0907\u0938 {version} \u0907\u0902\u0938\u094D\u091F\u0949\u0932 \u0939\u094B \u0917\u092F\u093E\u0964 \u0924\u0948\u092F\u093E\u0930 \u0939\u094B\u0928\u0947 \u092A\u0930 \u092A\u0947\u091C \u0930\u0940\u0932\u094B\u0921 \u0915\u0930\u0947\u0902\u0964",
|
|
2923
3071
|
"dsh-live-voice.commons.update.failed": "\u0905\u092A\u0921\u0947\u091F \u0935\u093F\u092B\u0932\u0964 \u0930\u093F\u0932\u0940\u091C\u093C \u0905\u092D\u0940 npm \u092A\u0930 \u0909\u092A\u0932\u092C\u094D\u0927 \u0928\u0939\u0940\u0902 \u0939\u094B \u0938\u0915\u0924\u0940\u0964 DSH \u092A\u094D\u0932\u0917\u0907\u0928 \u092A\u094D\u0930\u092C\u0902\u0927\u0915 \u0926\u0947\u0916\u0947\u0902\u0964",
|
|
3072
|
+
"dsh-live-voice.commons.update.incompatible": "\u092F\u0939 \u0930\u093F\u0932\u0940\u091C\u093C \u091A\u0932 \u0930\u0939\u0947 DSH \u0938\u0902\u0938\u094D\u0915\u0930\u0923 \u0915\u0947 \u0938\u093E\u0925 \u0938\u0902\u0917\u0924 \u0928\u0939\u0940\u0902 \u0939\u0948\u0964 DSH \u092A\u094D\u0932\u0917\u0907\u0928 \u092A\u094D\u0930\u092C\u0902\u0927\u0915 \u0926\u0947\u0916\u0947\u0902 \u0914\u0930 \u0938\u0902\u0917\u0924 DSH \u0924\u0925\u093E \u092A\u094D\u0932\u0917\u0907\u0928 \u0938\u0902\u0938\u094D\u0915\u0930\u0923 \u0909\u092A\u092F\u094B\u0917 \u0915\u0930\u0947\u0902; \u0915\u094B\u0908 \u0938\u0902\u0917\u0924\u0924\u093E \u091B\u0942\u091F \u0928\u0939\u0940\u0902 \u0926\u0940 \u0917\u0908\u0964",
|
|
2924
3073
|
"dsh-live-voice.commons.update.installing": "\u0905\u092A\u0921\u0947\u091F \u0939\u094B \u0930\u0939\u093E \u0939\u0948\u2026",
|
|
2925
3074
|
"dsh-live-voice.commons.update.label": "\u0905\u092A\u0921\u0947\u091F \u0909\u092A\u0932\u092C\u094D\u0927 \u0939\u0948",
|
|
2926
3075
|
"dsh-live-voice.commons.update.link": "\u0905\u092A\u0921\u0947\u091F \u0909\u092A\u0932\u092C\u094D\u0927 \u0939\u0948: {version}\u0964 \u0930\u093F\u0932\u0940\u091C\u093C \u0916\u094B\u0932\u0947\u0902",
|
|
@@ -2990,6 +3139,9 @@ var hi = {
|
|
|
2990
3139
|
"dsh-live-voice.recognition.commands.send": "\u091A\u0932 \u0930\u0939\u0947 \u090F\u091C\u0947\u0902\u091F \u0915\u094B \u092D\u0947\u091C\u0947\u0902",
|
|
2991
3140
|
"dsh-live-voice.recognition.commands.stopSpeech": "\u0938\u0939\u093E\u092F\u0915 \u0915\u093E \u0935\u093E\u091A\u0928 \u092C\u0902\u0926 \u0915\u0930\u0947\u0902",
|
|
2992
3141
|
"dsh-live-voice.recognition.commands.title": "\u0935\u0949\u0907\u0938 \u0915\u092E\u093E\u0902\u0921",
|
|
3142
|
+
"dsh-live-voice.recognition.context.hint": "\u0935\u0948\u0915\u0932\u094D\u092A\u093F\u0915 \u0938\u0902\u0926\u0930\u094D\u092D \u0914\u0930 \u0936\u092C\u094D\u0926\u093E\u0935\u0932\u0940 \u0939\u0930 \u0911\u0921\u093F\u092F\u094B \u0916\u0902\u0921 \u0915\u0947 \u0938\u093E\u0925 \u091A\u0941\u0928\u0940 \u0917\u0908 HTTP \u0935\u093E\u0915\u094D \u0938\u0947\u0935\u093E \u0915\u094B prompt \u0915\u0947 \u0930\u0942\u092A \u092E\u0947\u0902 \u092D\u0947\u091C\u0947 \u091C\u093E\u0924\u0947 \u0939\u0948\u0902\u0964 \u092B\u093C\u0940\u0932\u094D\u0921 \u0938\u0947 \u092C\u093E\u0939\u0930 \u0906\u0928\u0947 \u092A\u0930 \u0938\u0939\u0947\u091C\u093E \u091C\u093E\u0924\u093E \u0939\u0948\u0964 \u0938\u092E\u0930\u094D\u0925\u0928 \u0938\u0930\u094D\u0935\u0930 \u092A\u0930 \u0928\u093F\u0930\u094D\u092D\u0930 \u0939\u0948; \u092F\u0939 \u092D\u093E\u0937\u093E\u0913\u0902 \u0915\u094B \u0938\u0940\u092E\u093F\u0924 \u0928\u0939\u0940\u0902 \u0915\u0930\u0924\u093E \u0914\u0930 \u0928\u093F\u0930\u094D\u0926\u0947\u0936\u094B\u0902 \u0915\u0947 \u092A\u093E\u0932\u0928 \u0915\u0940 \u0917\u093E\u0930\u0902\u091F\u0940 \u0928\u0939\u0940\u0902 \u0926\u0947\u0924\u093E\u0964 \u0938\u0902\u0926\u0930\u094D\u092D \u0928 \u092D\u0947\u091C\u0928\u0947 \u0915\u0947 \u0932\u093F\u090F \u0916\u093E\u0932\u0940 \u091B\u094B\u0921\u093C\u0947\u0902\u0964",
|
|
3143
|
+
"dsh-live-voice.recognition.context.label": "\u091F\u094D\u0930\u093E\u0902\u0938\u0915\u094D\u0930\u093F\u092A\u094D\u0936\u0928 \u0938\u0902\u0926\u0930\u094D\u092D",
|
|
3144
|
+
"dsh-live-voice.recognition.context.placeholder": "Native Brazilian Portuguese speaker who frequently uses technical terms in English.",
|
|
2993
3145
|
"dsh-live-voice.recognition.dictation.cancel": "\u0921\u093F\u0915\u094D\u091F\u0947\u0936\u0928 \u0930\u0926\u094D\u0926 \u0915\u0930\u0947\u0902",
|
|
2994
3146
|
"dsh-live-voice.recognition.engine.label": "\u0935\u093E\u0915\u094D \u092A\u0939\u091A\u093E\u0928 \u0907\u0902\u091C\u0928",
|
|
2995
3147
|
"dsh-live-voice.recognition.headphoneMode.help": "\u092A\u094D\u0932\u0947\u092C\u0948\u0915 \u0915\u0947 \u0926\u094C\u0930\u093E\u0928 \u092E\u093E\u0907\u0915\u094D\u0930\u094B\u092B\u093C\u094B\u0928 \u091A\u093E\u0932\u0942 \u0930\u0939\u0924\u093E \u0939\u0948\u0964 \u092C\u093F\u0928\u093E \u092A\u094D\u0930\u0924\u093F\u0927\u094D\u0935\u0928\u093F \u0915\u0940 \u092A\u0939\u091A\u093E\u0928\u0940 \u0917\u0908 \u0906\u0935\u093E\u091C\u093C \u0928\u093F\u0930\u094D\u0927\u093E\u0930\u093F\u0924 \u0935\u093F\u0932\u0902\u092C \u0915\u0947 \u092C\u093E\u0926 \u092A\u094D\u0932\u0947\u092C\u0948\u0915 \u0930\u094B\u0915 \u0938\u0915\u0924\u0940 \u0939\u0948; \u092E\u093E\u0907\u0915\u094D\u0930\u094B\u092B\u093C\u094B\u0928 \u0914\u0930 \u0938\u093E\u091D\u093E \u092E\u0940\u091F\u093F\u0902\u0917 \u0911\u0921\u093F\u092F\u094B \u0909\u0924\u0928\u0940 \u0926\u0947\u0930 \u0936\u093E\u0902\u0924 \u0930\u0939\u0928\u0947 \u092A\u0930 \u092F\u0939 \u092B\u093F\u0930 \u091A\u0932\u0924\u093E \u0939\u0948\u0964 \u092E\u0948\u0928\u094D\u092F\u0941\u0905\u0932 \u0935\u093F\u0930\u093E\u092E \u0915\u0947 \u092C\u093E\u0926 \u092B\u093F\u0930 \u091A\u0932\u093E\u090F\u0901 \u0926\u092C\u093E\u090F\u0901\u0964",
|
|
@@ -3262,7 +3414,7 @@ var hi = {
|
|
|
3262
3414
|
"dsh-live-voice.speak.rate.label": "\u092C\u094B\u0932\u0928\u0947 \u0915\u0940 \u0917\u0924\u093F",
|
|
3263
3415
|
"dsh-live-voice.speak.responseDelay.help": "\u0906\u092A\u0915\u0947 \u092C\u094B\u0932\u0928\u093E \u092C\u0902\u0926 \u0915\u0930\u0928\u0947 \u0915\u0947 \u092C\u093E\u0926 \u0938\u0939\u093E\u092F\u0915 \u0915\u093E \u0938\u094D\u0935\u091A\u093E\u0932\u093F\u0924 \u092A\u094D\u0932\u0947\u092C\u0948\u0915 \u0907\u0924\u0928\u0940 \u0926\u0947\u0930 \u0924\u0915 \u0932\u0917\u093E\u0924\u093E\u0930 \u092E\u094C\u0928 \u0930\u0939\u0928\u0947 \u0915\u0940 \u092A\u094D\u0930\u0924\u0940\u0915\u094D\u0937\u093E \u0915\u0930\u0924\u093E \u0939\u0948\u0964 \u0926\u094B\u092C\u093E\u0930\u093E \u092C\u094B\u0932\u0928\u0947 \u092A\u0930 \u092A\u094D\u0930\u0924\u0940\u0915\u094D\u0937\u093E \u092B\u093F\u0930 \u0938\u0947 \u0936\u0941\u0930\u0942 \u0939\u094B\u0924\u0940 \u0939\u0948\u0964",
|
|
3264
3416
|
"dsh-live-voice.speak.responseDelay.label": "\u0938\u0939\u093E\u092F\u0915 \u0915\u0947 \u091C\u0935\u093E\u092C \u0915\u093E \u0935\u093F\u0932\u0902\u092C",
|
|
3265
|
-
"dsh-live-voice.speak.responseDelay.
|
|
3417
|
+
"dsh-live-voice.speak.responseDelay.oneAndHalf": "1.5 \u0938\u0947\u0915\u0902\u0921",
|
|
3266
3418
|
"dsh-live-voice.speak.segmentGap.help": "\u0932\u0917\u093E\u0924\u093E\u0930 \u092C\u094B\u0932\u0947 \u0917\u090F \u0939\u093F\u0938\u094D\u0938\u094B\u0902 \u0915\u0947 \u092C\u0940\u091A \u0907\u0924\u0928\u0947 \u092E\u093F\u0932\u0940\u0938\u0947\u0915\u0902\u0921 \u0930\u0941\u0915\u0947\u0902\u0964 \u0921\u093F\u092B\u093C\u0949\u0932\u094D\u091F 400 ms \u0939\u0948\u0964",
|
|
3267
3419
|
"dsh-live-voice.speak.segmentGap.label": "\u092C\u094B\u0932\u0947 \u0917\u090F \u0939\u093F\u0938\u094D\u0938\u094B\u0902 \u0915\u0947 \u092C\u0940\u091A \u0935\u093F\u0930\u093E\u092E",
|
|
3268
3420
|
"dsh-live-voice.speak.status.paused": "\u0935\u093E\u091A\u0928 \u0920\u0939\u0930\u093E \u0939\u0941\u0906 \u0939\u0948",
|
|
@@ -3314,8 +3466,10 @@ var ptBR = {
|
|
|
3314
3466
|
"dsh-live-voice.commons.update.cancel": "Cancelar",
|
|
3315
3467
|
"dsh-live-voice.commons.update.confirm": "Instalar Voz ao Vivo {version} neste perfil do DSH? O download precisa de rede. Reinicie o DSH depois, se solicitado; nada reinicia automaticamente.",
|
|
3316
3468
|
"dsh-live-voice.commons.update.current": "Esta vers\xE3o j\xE1 est\xE1 instalada. Reinicie o DSH para utiliz\xE1-la.",
|
|
3469
|
+
"dsh-live-voice.commons.update.delayed": "A vers\xE3o solicitada ainda n\xE3o est\xE1 dispon\xEDvel para este perfil. A publica\xE7\xE3o pode estar em andamento ou aguardando o minimumReleaseAge do pnpm. Aguarde e consulte o gerenciador de plugins do DSH; nenhuma pol\xEDtica de seguran\xE7a foi ignorada.",
|
|
3317
3470
|
"dsh-live-voice.commons.update.done": "Voz ao Vivo {version} instalado. Recarregue a p\xE1gina quando quiser.",
|
|
3318
3471
|
"dsh-live-voice.commons.update.failed": "Falha na atualiza\xE7\xE3o. A vers\xE3o pode ainda n\xE3o estar no npm. Verifique o gerenciador de plugins do DSH.",
|
|
3472
|
+
"dsh-live-voice.commons.update.incompatible": "Esta vers\xE3o \xE9 incompat\xEDvel com o DSH em execu\xE7\xE3o. Consulte o gerenciador de plugins e use vers\xF5es compat\xEDveis do DSH e do plugin; nenhuma exce\xE7\xE3o de compatibilidade foi concedida.",
|
|
3319
3473
|
"dsh-live-voice.commons.update.installing": "Atualizando\u2026",
|
|
3320
3474
|
"dsh-live-voice.commons.update.label": "Atualiza\xE7\xE3o dispon\xEDvel",
|
|
3321
3475
|
"dsh-live-voice.commons.update.link": "Atualiza\xE7\xE3o dispon\xEDvel: {version}. Abrir lan\xE7amento",
|
|
@@ -3385,6 +3539,9 @@ var ptBR = {
|
|
|
3385
3539
|
"dsh-live-voice.recognition.commands.send": "Enviar ao agente em execu\xE7\xE3o",
|
|
3386
3540
|
"dsh-live-voice.recognition.commands.stopSpeech": "Parar fala do assistente",
|
|
3387
3541
|
"dsh-live-voice.recognition.commands.title": "Comandos de voz",
|
|
3542
|
+
"dsh-live-voice.recognition.context.hint": "Contexto e vocabul\xE1rio opcionais enviados como prompt ao servi\xE7o HTTP de voz selecionado, junto com cada trecho de \xE1udio. Salvo ao sair do campo. O suporte depende do servidor; n\xE3o restringe idiomas nem garante que instru\xE7\xF5es sejam seguidas. Deixe vazio para n\xE3o enviar contexto.",
|
|
3543
|
+
"dsh-live-voice.recognition.context.label": "Contexto da transcri\xE7\xE3o",
|
|
3544
|
+
"dsh-live-voice.recognition.context.placeholder": "Native Brazilian Portuguese speaker who frequently uses technical terms in English.",
|
|
3388
3545
|
"dsh-live-voice.recognition.dictation.cancel": "Cancelar ditado",
|
|
3389
3546
|
"dsh-live-voice.recognition.engine.label": "Mecanismo de reconhecimento",
|
|
3390
3547
|
"dsh-live-voice.recognition.headphoneMode.help": "O microfone fica aberto durante a reprodu\xE7\xE3o. Fala reconhecida sem eco pode pausar ap\xF3s o atraso configurado; a reprodu\xE7\xE3o retoma quando microfone e \xE1udio compartilhado da reuni\xE3o ficam em sil\xEAncio por esse intervalo. Pausa manual exige Retomar.",
|
|
@@ -3657,7 +3814,7 @@ var ptBR = {
|
|
|
3657
3814
|
"dsh-live-voice.speak.rate.label": "Velocidade da fala",
|
|
3658
3815
|
"dsh-live-voice.speak.responseDelay.help": "Depois que voc\xEA parar de falar, a reprodu\xE7\xE3o autom\xE1tica do assistente aguardar\xE1 esse tempo de sil\xEAncio cont\xEDnuo. Falar novamente reinicia a contagem.",
|
|
3659
3816
|
"dsh-live-voice.speak.responseDelay.label": "Atraso da resposta do assistente",
|
|
3660
|
-
"dsh-live-voice.speak.responseDelay.
|
|
3817
|
+
"dsh-live-voice.speak.responseDelay.oneAndHalf": "1,5 segundo",
|
|
3661
3818
|
"dsh-live-voice.speak.segmentGap.help": "Aguarde esta quantidade de milissegundos entre trechos falados consecutivos. O padr\xE3o \xE9 400 ms.",
|
|
3662
3819
|
"dsh-live-voice.speak.segmentGap.label": "Pausa entre trechos de fala",
|
|
3663
3820
|
"dsh-live-voice.speak.status.paused": "Fala pausada",
|
|
@@ -3709,8 +3866,10 @@ var zh = {
|
|
|
3709
3866
|
"dsh-live-voice.commons.update.cancel": "\u53D6\u6D88",
|
|
3710
3867
|
"dsh-live-voice.commons.update.confirm": "\u5728\u6B64 DSH \u914D\u7F6E\u4E2D\u5B89\u88C5 \u5B9E\u65F6\u8BED\u97F3 {version}\uFF1F\u4E0B\u8F7D\u9700\u8981\u7F51\u7EDC\u3002\u82E5\u63D0\u793A\uFF0C\u8BF7\u4E4B\u540E\u91CD\u542F DSH\uFF1B\u4E0D\u4F1A\u81EA\u52A8\u91CD\u542F\u3002",
|
|
3711
3868
|
"dsh-live-voice.commons.update.current": "\u6B64\u7248\u672C\u5DF2\u5B89\u88C5\u3002\u8BF7\u91CD\u542F DSH \u540E\u4F7F\u7528\u3002",
|
|
3869
|
+
"dsh-live-voice.commons.update.delayed": "\u6B64\u914D\u7F6E\u6682\u65F6\u65E0\u6CD5\u83B7\u53D6\u6240\u8BF7\u6C42\u7684\u7248\u672C\u3002\u5B83\u53EF\u80FD\u4ECD\u5728\u53D1\u5E03\uFF0C\u6216\u53D7 pnpm minimumReleaseAge \u9650\u5236\u3002\u8BF7\u7A0D\u540E\u68C0\u67E5 DSH \u63D2\u4EF6\u7BA1\u7406\u5668\uFF1B\u672A\u7ED5\u8FC7\u4EFB\u4F55\u5B89\u5168\u7B56\u7565\u3002",
|
|
3712
3870
|
"dsh-live-voice.commons.update.done": "\u5B9E\u65F6\u8BED\u97F3 {version} \u5DF2\u5B89\u88C5\u3002\u51C6\u5907\u597D\u540E\u8BF7\u5237\u65B0\u9875\u9762\u3002",
|
|
3713
3871
|
"dsh-live-voice.commons.update.failed": "\u66F4\u65B0\u5931\u8D25\u3002\u8BE5\u7248\u672C\u53EF\u80FD\u5C1A\u672A\u53D1\u5E03\u5230 npm\u3002\u8BF7\u67E5\u770B DSH \u63D2\u4EF6\u7BA1\u7406\u5668\u3002",
|
|
3872
|
+
"dsh-live-voice.commons.update.incompatible": "\u6B64\u7248\u672C\u4E0E\u6B63\u5728\u8FD0\u884C\u7684 DSH \u4E0D\u517C\u5BB9\u3002\u8BF7\u68C0\u67E5 DSH \u63D2\u4EF6\u7BA1\u7406\u5668\u5E76\u4F7F\u7528\u517C\u5BB9\u7684 DSH \u548C\u63D2\u4EF6\u7248\u672C\uFF1B\u672A\u6388\u4E88\u517C\u5BB9\u6027\u8C41\u514D\u3002",
|
|
3714
3873
|
"dsh-live-voice.commons.update.installing": "\u6B63\u5728\u66F4\u65B0\u2026",
|
|
3715
3874
|
"dsh-live-voice.commons.update.label": "\u6709\u53EF\u7528\u66F4\u65B0",
|
|
3716
3875
|
"dsh-live-voice.commons.update.link": "\u6709\u53EF\u7528\u66F4\u65B0\uFF1A{version}\u3002\u6253\u5F00\u53D1\u5E03\u9875\u9762",
|
|
@@ -3780,6 +3939,9 @@ var zh = {
|
|
|
3780
3939
|
"dsh-live-voice.recognition.commands.send": "\u53D1\u9001\u7ED9\u8FD0\u884C\u4E2D\u7684\u667A\u80FD\u4F53",
|
|
3781
3940
|
"dsh-live-voice.recognition.commands.stopSpeech": "\u505C\u6B62\u52A9\u624B\u6717\u8BFB",
|
|
3782
3941
|
"dsh-live-voice.recognition.commands.title": "\u8BED\u97F3\u547D\u4EE4",
|
|
3942
|
+
"dsh-live-voice.recognition.context.hint": "\u53EF\u9009\u7684\u80CC\u666F\u548C\u8BCD\u6C47\u4F1A\u968F\u6BCF\u6BB5\u97F3\u9891\u4EE5 prompt \u5B57\u6BB5\u53D1\u9001\u5230\u6240\u9009 HTTP \u8BED\u97F3\u670D\u52A1\u3002\u79BB\u5F00\u8F93\u5165\u6846\u65F6\u4FDD\u5B58\u3002\u652F\u6301\u60C5\u51B5\u53D6\u51B3\u4E8E\u670D\u52A1\u5668\uFF1B\u4E0D\u4F1A\u9650\u5236\u8BED\u8A00\uFF0C\u4E5F\u4E0D\u4FDD\u8BC1\u9075\u5FAA\u6307\u4EE4\u3002\u7559\u7A7A\u5219\u4E0D\u53D1\u9001\u4E0A\u4E0B\u6587\u3002",
|
|
3943
|
+
"dsh-live-voice.recognition.context.label": "\u8F6C\u5199\u4E0A\u4E0B\u6587",
|
|
3944
|
+
"dsh-live-voice.recognition.context.placeholder": "Native Brazilian Portuguese speaker who frequently uses technical terms in English.",
|
|
3783
3945
|
"dsh-live-voice.recognition.dictation.cancel": "\u53D6\u6D88\u542C\u5199",
|
|
3784
3946
|
"dsh-live-voice.recognition.engine.label": "\u8BC6\u522B\u5F15\u64CE",
|
|
3785
3947
|
"dsh-live-voice.recognition.headphoneMode.help": "\u64AD\u653E\u65F6\u9EA6\u514B\u98CE\u4FDD\u6301\u5F00\u542F\u3002\u8BC6\u522B\u5230\u975E\u56DE\u58F0\u8BED\u97F3\u540E\uFF0C\u53EF\u5728\u8BBE\u5B9A\u5EF6\u8FDF\u540E\u6682\u505C\u64AD\u653E\uFF1B\u9EA6\u514B\u98CE\u548C\u5171\u4EAB\u4F1A\u8BAE\u97F3\u9891\u6301\u7EED\u9759\u97F3\u8FBE\u5230\u8BE5\u65F6\u957F\u540E\u6062\u590D\u3002\u624B\u52A8\u6682\u505C\u540E\u9700\u70B9\u51FB\u6062\u590D\u3002",
|
|
@@ -4052,7 +4214,7 @@ var zh = {
|
|
|
4052
4214
|
"dsh-live-voice.speak.rate.label": "\u8BED\u901F",
|
|
4053
4215
|
"dsh-live-voice.speak.responseDelay.help": "\u4F60\u505C\u6B62\u8BF4\u8BDD\u540E\uFF0C\u52A9\u624B\u4F1A\u7B49\u5F85\u6B64\u65F6\u957F\u7684\u8FDE\u7EED\u9759\u97F3\uFF0C\u7136\u540E\u81EA\u52A8\u64AD\u653E\u8BED\u97F3\u3002\u518D\u6B21\u8BF4\u8BDD\u4F1A\u91CD\u65B0\u5F00\u59CB\u7B49\u5F85\u3002",
|
|
4054
4216
|
"dsh-live-voice.speak.responseDelay.label": "\u52A9\u624B\u54CD\u5E94\u5EF6\u8FDF",
|
|
4055
|
-
"dsh-live-voice.speak.responseDelay.
|
|
4217
|
+
"dsh-live-voice.speak.responseDelay.oneAndHalf": "1.5 \u79D2",
|
|
4056
4218
|
"dsh-live-voice.speak.segmentGap.help": "\u5728\u8FDE\u7EED\u8BED\u97F3\u7247\u6BB5\u4E4B\u95F4\u7B49\u5F85\u6B64\u6BEB\u79D2\u6570\u3002\u9ED8\u8BA4\u503C\u4E3A 400 ms\u3002",
|
|
4057
4219
|
"dsh-live-voice.speak.segmentGap.label": "\u8BED\u97F3\u7247\u6BB5\u4E4B\u95F4\u7684\u505C\u987F",
|
|
4058
4220
|
"dsh-live-voice.speak.status.paused": "\u8BED\u97F3\u5DF2\u6682\u505C",
|
|
@@ -5050,6 +5212,19 @@ function SpeechStatusBar({ controller }) {
|
|
|
5050
5212
|
));
|
|
5051
5213
|
}
|
|
5052
5214
|
|
|
5215
|
+
// src/modules/recognition/transcriptionContext.ts
|
|
5216
|
+
var TRANSCRIPTION_CONTEXT_MAX_LENGTH = 1e3;
|
|
5217
|
+
function transcriptionContext(value) {
|
|
5218
|
+
if (value === void 0 || value === null) return "";
|
|
5219
|
+
if (typeof value !== "string" || value.length > TRANSCRIPTION_CONTEXT_MAX_LENGTH || value.includes("\0"))
|
|
5220
|
+
throw new Error("Transcription context must contain at most 1000 characters without NUL.");
|
|
5221
|
+
return value.trim();
|
|
5222
|
+
}
|
|
5223
|
+
function transcriptionContextHeader(value) {
|
|
5224
|
+
const text = transcriptionContext(value);
|
|
5225
|
+
return text ? { "x-dlv-transcription-context": encodeURIComponent(text) } : {};
|
|
5226
|
+
}
|
|
5227
|
+
|
|
5053
5228
|
// src/modules/core/speechDefaults.ts
|
|
5054
5229
|
var notices = {
|
|
5055
5230
|
en: "You can see the code in our conversation history.",
|
|
@@ -5107,6 +5282,7 @@ var defaultQwenVoice = qwenVoices[0].value;
|
|
|
5107
5282
|
var defaultAgentVoiceContext = `Live Voice output is active. Your entire user-facing response will be spoken aloud.
|
|
5108
5283
|
Be concise and conversational. Lead with the answer or next action. Avoid unnecessary repetition, long preambles, dense lists, raw code, paths, and verbose status narration.
|
|
5109
5284
|
Do not narrate routine tool activity by default. If the user explicitly asks you to keep them informed while working, provide brief spoken progress updates only at meaningful milestones.`;
|
|
5285
|
+
var assistantSpeechDelayOptions = Object.freeze([0.6, 1.5, 2, 3, 4, 5]);
|
|
5110
5286
|
var autoSendDelayOptions = Object.freeze([0.6, 0.8, 1, 2, 3, 4, 5, 6]);
|
|
5111
5287
|
var recordingBitrateOptions = Object.freeze([24e3, 32e3, 48e3, 64e3, 96e3]);
|
|
5112
5288
|
var defaultSettings = Object.freeze({
|
|
@@ -5142,10 +5318,11 @@ var defaultSettings = Object.freeze({
|
|
|
5142
5318
|
sendingMode: "manual",
|
|
5143
5319
|
dshBusyEnterBehavior: "queue",
|
|
5144
5320
|
autoSendDelaySeconds: 4,
|
|
5145
|
-
assistantSpeechDelaySeconds:
|
|
5321
|
+
assistantSpeechDelaySeconds: 1.5,
|
|
5146
5322
|
mode: "speaker",
|
|
5147
5323
|
lang: "pt-BR",
|
|
5148
5324
|
recognitionLang: "pt-BR",
|
|
5325
|
+
recognitionContext: "",
|
|
5149
5326
|
voice: "",
|
|
5150
5327
|
inputDeviceId: "",
|
|
5151
5328
|
outputDeviceId: "",
|
|
@@ -5207,6 +5384,7 @@ function normalizeSettings(value) {
|
|
|
5207
5384
|
browserQwenModel: "qwen-0.6b",
|
|
5208
5385
|
browserAsrDevice: source.browserAsrDevice === "wasm" ? "wasm" : "webgpu",
|
|
5209
5386
|
recognitionProcessLocally: typeof source.recognitionProcessLocally === "boolean" ? source.recognitionProcessLocally : defaultSettings.recognitionProcessLocally,
|
|
5387
|
+
recognitionContext: typeof source.recognitionContext === "string" ? source.recognitionContext.replaceAll("\0", "").slice(0, TRANSCRIPTION_CONTEXT_MAX_LENGTH).trim() : defaultSettings.recognitionContext,
|
|
5210
5388
|
recognitionAutoInstall: typeof source.recognitionAutoInstall === "boolean" ? source.recognitionAutoInstall : defaultSettings.recognitionAutoInstall,
|
|
5211
5389
|
voiceDetectionEngine: ["energy", "silero"].includes(source.voiceDetectionEngine) ? source.voiceDetectionEngine : defaultSettings.voiceDetectionEngine,
|
|
5212
5390
|
voiceDetectionPreset: source.voiceDetectionPreset === "custom" || Object.hasOwn(voiceDetectionPresets, source.voiceDetectionPreset) ? source.voiceDetectionPreset : defaultSettings.voiceDetectionPreset,
|
|
@@ -5244,7 +5422,9 @@ function normalizeSettings(value) {
|
|
|
5244
5422
|
dshBusyEnterBehavior: ["queue", "steer"].includes(source.dshBusyEnterBehavior) ? source.dshBusyEnterBehavior : defaultSettings.dshBusyEnterBehavior,
|
|
5245
5423
|
sendingMode: source.sendingMode === "automatic" ? "queue" : ["manual", "queue", "steer"].includes(source.sendingMode) ? source.sendingMode : defaultSettings.sendingMode,
|
|
5246
5424
|
autoSendDelaySeconds: autoSendDelayOptions.includes(source.autoSendDelaySeconds) ? source.autoSendDelaySeconds : defaultSettings.autoSendDelaySeconds,
|
|
5247
|
-
assistantSpeechDelaySeconds:
|
|
5425
|
+
assistantSpeechDelaySeconds: assistantSpeechDelayOptions.includes(
|
|
5426
|
+
source.assistantSpeechDelaySeconds
|
|
5427
|
+
) ? source.assistantSpeechDelaySeconds : defaultSettings.assistantSpeechDelaySeconds,
|
|
5248
5428
|
mode: ["speaker", "headphones"].includes(source.mode) ? source.mode : defaultSettings.mode,
|
|
5249
5429
|
lang: typeof source.lang === "string" && /^[a-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/.test(source.lang) ? source.lang : defaultSettings.lang,
|
|
5250
5430
|
recognitionLang: source.recognitionLang === "auto" || typeof source.recognitionLang === "string" && /^[a-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/.test(source.recognitionLang) ? source.recognitionLang : typeof source.lang === "string" && /^[a-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/.test(source.lang) ? source.lang : defaultSettings.recognitionLang,
|
|
@@ -6869,8 +7049,23 @@ function cancellable(promise, signal) {
|
|
|
6869
7049
|
});
|
|
6870
7050
|
}
|
|
6871
7051
|
var VoiceCoordinator = class _VoiceCoordinator {
|
|
6872
|
-
constructor({
|
|
6873
|
-
|
|
7052
|
+
constructor({
|
|
7053
|
+
recognition,
|
|
7054
|
+
engines,
|
|
7055
|
+
meter,
|
|
7056
|
+
composer,
|
|
7057
|
+
settings = {},
|
|
7058
|
+
persistSettings,
|
|
7059
|
+
recordingTimeline
|
|
7060
|
+
}) {
|
|
7061
|
+
Object.assign(this, {
|
|
7062
|
+
recognition,
|
|
7063
|
+
engines,
|
|
7064
|
+
meter,
|
|
7065
|
+
composer,
|
|
7066
|
+
persistSettings,
|
|
7067
|
+
recordingTimeline
|
|
7068
|
+
});
|
|
6874
7069
|
this.listeners = /* @__PURE__ */ new Set();
|
|
6875
7070
|
this.transcript = new TranscriptDraft();
|
|
6876
7071
|
this.epoch = 0;
|
|
@@ -6896,6 +7091,7 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
6896
7091
|
recognizing: false,
|
|
6897
7092
|
pendingTranscriptions: 0,
|
|
6898
7093
|
pendingTranscriptionRequests: void 0,
|
|
7094
|
+
pendingDeliveryTranscriptions: void 0,
|
|
6899
7095
|
muted: normalizeSettings(settings).microphoneEnabled === false,
|
|
6900
7096
|
speaking: false,
|
|
6901
7097
|
paused: false,
|
|
@@ -7240,14 +7436,20 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
7240
7436
|
onResult: (result) => {
|
|
7241
7437
|
if (valid()) this.onResult(result);
|
|
7242
7438
|
},
|
|
7243
|
-
onProcessingChange: ({ pending = 0, transcribing } = {}) => {
|
|
7439
|
+
onProcessingChange: ({ pending = 0, transcribing, deliveryPending } = {}) => {
|
|
7244
7440
|
if (!valid()) return;
|
|
7245
7441
|
const count = Number.isSafeInteger(pending) && pending >= 0 ? pending : 0;
|
|
7246
7442
|
const requests = Number.isSafeInteger(transcribing) && transcribing >= 0 ? transcribing : count;
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
|
|
7443
|
+
const delivery = Number.isSafeInteger(deliveryPending) && deliveryPending >= 0 ? deliveryPending : count;
|
|
7444
|
+
this.patch({
|
|
7445
|
+
pendingTranscriptions: count,
|
|
7446
|
+
pendingTranscriptionRequests: requests,
|
|
7447
|
+
pendingDeliveryTranscriptions: delivery
|
|
7448
|
+
});
|
|
7449
|
+
if (delivery > 0) this.cancelAutoSend({ preserveIntent: true });
|
|
7450
|
+
else if (this.holdToTalkRelease) {
|
|
7451
|
+
if (count === 0) this._finishHoldToTalk();
|
|
7452
|
+
} else this.maybeScheduleAutoSend();
|
|
7251
7453
|
if (count === 0) this._drain();
|
|
7252
7454
|
},
|
|
7253
7455
|
onActivity: (active) => {
|
|
@@ -7448,15 +7650,18 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
7448
7650
|
this._handleSpeechInterruption(this._speechActivity());
|
|
7449
7651
|
this._drain();
|
|
7450
7652
|
}
|
|
7653
|
+
deliveryPending() {
|
|
7654
|
+
return this.snapshot.pendingDeliveryTranscriptions ?? this.snapshot.pendingTranscriptions;
|
|
7655
|
+
}
|
|
7451
7656
|
maybeScheduleAutoSend(draft = this.autoSendIntent) {
|
|
7452
7657
|
if (draft == null || draft !== this.composer.getDraft()) return;
|
|
7453
7658
|
if (this.autoSendTimer !== null) return;
|
|
7454
|
-
if (this.snapshot.settings.sendingMode === "manual" || this.snapshot.muted || this.composer.canAutoSend?.() === false || this.snapshot.recognizing || this.
|
|
7659
|
+
if (this.snapshot.settings.sendingMode === "manual" || this.snapshot.muted || this.composer.canAutoSend?.() === false || this.snapshot.recognizing || this.deliveryPending() > 0)
|
|
7455
7660
|
return;
|
|
7456
7661
|
this.scheduleAutoSend(draft);
|
|
7457
7662
|
}
|
|
7458
7663
|
refreshDeliveryReadiness() {
|
|
7459
|
-
if (this.composer.canAutoSend?.() === false || this.snapshot.recognizing || this.
|
|
7664
|
+
if (this.composer.canAutoSend?.() === false || this.snapshot.recognizing || this.deliveryPending() > 0)
|
|
7460
7665
|
this.cancelAutoSend({ preserveIntent: true });
|
|
7461
7666
|
else this.maybeScheduleAutoSend();
|
|
7462
7667
|
}
|
|
@@ -7513,6 +7718,7 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
7513
7718
|
recognizing: false,
|
|
7514
7719
|
pendingTranscriptions: 0,
|
|
7515
7720
|
pendingTranscriptionRequests: 0,
|
|
7721
|
+
pendingDeliveryTranscriptions: 0,
|
|
7516
7722
|
starting: false
|
|
7517
7723
|
});
|
|
7518
7724
|
return this._input(async () => {
|
|
@@ -7673,6 +7879,7 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
7673
7879
|
this.queue = [];
|
|
7674
7880
|
}
|
|
7675
7881
|
async stopSpeech(resumeListening = true) {
|
|
7882
|
+
this.activeRecordingTimeline = null;
|
|
7676
7883
|
this._cancelAssistantSpeechTimer();
|
|
7677
7884
|
this.assistantSpeechNotBefore = 0;
|
|
7678
7885
|
this._cancelInterruptionResume();
|
|
@@ -7780,8 +7987,10 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
7780
7987
|
try {
|
|
7781
7988
|
const result = await this.engines[this.snapshot.speechEngine || this.snapshot.settings.engine].pause();
|
|
7782
7989
|
if (result !== false) this._cancelSpeechPrefetch();
|
|
7783
|
-
if (epoch === this.speechEpoch && control === this.controlEpoch && this.snapshot.speaking && result !== false)
|
|
7990
|
+
if (epoch === this.speechEpoch && control === this.controlEpoch && this.snapshot.speaking && result !== false) {
|
|
7784
7991
|
this.patch({ paused: true });
|
|
7992
|
+
this.activeRecordingTimeline?.("assistant-pause");
|
|
7993
|
+
}
|
|
7785
7994
|
} catch (error) {
|
|
7786
7995
|
if (epoch === this.speechEpoch) this.patch({ error: message(error) });
|
|
7787
7996
|
}
|
|
@@ -7821,6 +8030,7 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
7821
8030
|
const result = await this.engines[this.snapshot.speechEngine || this.snapshot.settings.engine].resume();
|
|
7822
8031
|
if (epoch === this.speechEpoch && control === this.controlEpoch && result !== false) {
|
|
7823
8032
|
this.patch({ paused: false });
|
|
8033
|
+
this.activeRecordingTimeline?.("assistant-resume");
|
|
7824
8034
|
this._prefetchSpeech();
|
|
7825
8035
|
}
|
|
7826
8036
|
} catch (error) {
|
|
@@ -7864,6 +8074,10 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
7864
8074
|
const controller = new AbortController();
|
|
7865
8075
|
this.speechController = controller;
|
|
7866
8076
|
this.speechLaunchPending = true;
|
|
8077
|
+
const timeline = this.activeRecordingTimeline ?? this.recordingTimeline?.();
|
|
8078
|
+
this.activeRecordingTimeline = timeline;
|
|
8079
|
+
let playbackStarted = false;
|
|
8080
|
+
let playbackFinished = false;
|
|
7867
8081
|
const engine = this.engines[this.snapshot.settings.engine];
|
|
7868
8082
|
this._resetStaleSpeechItem(item, this._speechPreparationKey());
|
|
7869
8083
|
item.engine = engine;
|
|
@@ -7888,6 +8102,7 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
7888
8102
|
if (this.speechStopError) throw this.speechStopError;
|
|
7889
8103
|
if (epoch !== this.speechEpoch || this.disposed) return;
|
|
7890
8104
|
if (this.snapshot.settings.mode === "speaker") {
|
|
8105
|
+
if (this.snapshot.listening) timeline?.("capture-gated");
|
|
7891
8106
|
await this.stopListening();
|
|
7892
8107
|
if (this.inputReleaseError) throw this.inputReleaseError;
|
|
7893
8108
|
}
|
|
@@ -7898,7 +8113,11 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
7898
8113
|
if (queued?.prepareError) throw queued.prepareError;
|
|
7899
8114
|
const options = {
|
|
7900
8115
|
onPlaybackStart: () => {
|
|
7901
|
-
if (epoch === this.speechEpoch && !this.disposed
|
|
8116
|
+
if (epoch === this.speechEpoch && !this.disposed && !controller.signal.aborted && !playbackFinished) {
|
|
8117
|
+
if (!playbackStarted) timeline?.("assistant-start");
|
|
8118
|
+
playbackStarted = true;
|
|
8119
|
+
this.patch({ speechLoading: false });
|
|
8120
|
+
}
|
|
7902
8121
|
},
|
|
7903
8122
|
voice: this.snapshot.settings.voice || void 0,
|
|
7904
8123
|
rate: this.snapshot.settings.rate,
|
|
@@ -7931,6 +8150,10 @@ var VoiceCoordinator = class _VoiceCoordinator {
|
|
|
7931
8150
|
if (error.name !== "AbortError") this.patch({ error: message(error) });
|
|
7932
8151
|
}
|
|
7933
8152
|
} finally {
|
|
8153
|
+
playbackFinished = true;
|
|
8154
|
+
if (playbackStarted) timeline?.("assistant-end");
|
|
8155
|
+
if (this.activeRecordingTimeline === timeline && epoch === this.speechEpoch && !this.queue.length)
|
|
8156
|
+
this.activeRecordingTimeline = null;
|
|
7934
8157
|
if (epoch === this.speechEpoch && !this.disposed) {
|
|
7935
8158
|
this.patch({
|
|
7936
8159
|
speaking: false,
|
|
@@ -8089,6 +8312,8 @@ var MeetingController = class {
|
|
|
8089
8312
|
if (Object.hasOwn(next, "pending")) policy.pendingTranscriptions = next.pending;
|
|
8090
8313
|
if (Object.hasOwn(next, "transcribing"))
|
|
8091
8314
|
policy.pendingTranscriptionRequests = next.transcribing;
|
|
8315
|
+
if (Object.hasOwn(next, "deliveryPending"))
|
|
8316
|
+
policy.pendingDeliveryTranscriptions = next.deliveryPending;
|
|
8092
8317
|
input?.patch(policy);
|
|
8093
8318
|
}
|
|
8094
8319
|
this.snapshot = {
|
|
@@ -8261,11 +8486,12 @@ var MeetingController = class {
|
|
|
8261
8486
|
this.onActivity?.(source, active);
|
|
8262
8487
|
input.refreshDeliveryReadiness();
|
|
8263
8488
|
},
|
|
8264
|
-
onProcessingChange: ({ pending, transcribing }) => {
|
|
8489
|
+
onProcessingChange: ({ pending, transcribing, deliveryPending }) => {
|
|
8265
8490
|
if (!valid()) return;
|
|
8266
8491
|
const requests = Number.isSafeInteger(transcribing) && transcribing >= 0 ? transcribing : pending;
|
|
8267
|
-
|
|
8268
|
-
|
|
8492
|
+
const delivery = Number.isSafeInteger(deliveryPending) && deliveryPending >= 0 ? deliveryPending : pending;
|
|
8493
|
+
this.patch(source, { pending, transcribing: requests, deliveryPending: delivery });
|
|
8494
|
+
if (delivery) input.cancelAutoSend({ preserveIntent: true });
|
|
8269
8495
|
else if (!input.getSnapshot().muted) input.maybeScheduleAutoSend();
|
|
8270
8496
|
},
|
|
8271
8497
|
onError: (error) => {
|
|
@@ -8308,12 +8534,19 @@ var MeetingController = class {
|
|
|
8308
8534
|
listening: false,
|
|
8309
8535
|
recognizing: false,
|
|
8310
8536
|
pendingTranscriptions: 0,
|
|
8311
|
-
pendingTranscriptionRequests: 0
|
|
8537
|
+
pendingTranscriptionRequests: 0,
|
|
8538
|
+
pendingDeliveryTranscriptions: 0
|
|
8312
8539
|
});
|
|
8313
8540
|
this.onActivity?.(source, false);
|
|
8314
8541
|
job?.abort.abort();
|
|
8315
8542
|
this.transcript.setActive(source, false);
|
|
8316
|
-
this.patch(source, {
|
|
8543
|
+
this.patch(source, {
|
|
8544
|
+
starting: false,
|
|
8545
|
+
listening: false,
|
|
8546
|
+
pending: 0,
|
|
8547
|
+
transcribing: 0,
|
|
8548
|
+
deliveryPending: 0
|
|
8549
|
+
});
|
|
8317
8550
|
await job?.engine?.stop();
|
|
8318
8551
|
await job?.meter?.stop();
|
|
8319
8552
|
}
|
|
@@ -9494,6 +9727,12 @@ var PinDetector = class {
|
|
|
9494
9727
|
}
|
|
9495
9728
|
} else this.candidate = 0;
|
|
9496
9729
|
}
|
|
9730
|
+
/** Advance past unclassified saved samples without inventing a speech/silence transition. */
|
|
9731
|
+
skipTo(position) {
|
|
9732
|
+
this.at = Math.max(this.at, position);
|
|
9733
|
+
this.candidate = 0;
|
|
9734
|
+
this.quiet = 0;
|
|
9735
|
+
}
|
|
9497
9736
|
finish() {
|
|
9498
9737
|
if (this.speaking) {
|
|
9499
9738
|
this.speaking = false;
|
|
@@ -9505,6 +9744,7 @@ var PinDetector = class {
|
|
|
9505
9744
|
|
|
9506
9745
|
// src/modules/recording/RecordingRecognition.ts
|
|
9507
9746
|
var asError = (value) => value instanceof Error ? value : new Error(String(value));
|
|
9747
|
+
var TRANSCRIPTION_BOUNDARY_MARGIN_MS = 800;
|
|
9508
9748
|
var uid = () => crypto.randomUUID();
|
|
9509
9749
|
async function startRecordingRecognition(engine, options, capture) {
|
|
9510
9750
|
const { signal, lang = engine.lang, onResult, onActivity, onError, onProcessingChange } = options;
|
|
@@ -9513,8 +9753,8 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9513
9753
|
enabled: engine.recordingEnabled !== false,
|
|
9514
9754
|
keepPrevious: engine.recordingKeepPrevious ?? 1,
|
|
9515
9755
|
bitrate: engine.recordingBitrate ?? 48e3,
|
|
9516
|
-
preRollMs: engine.recordingPreRollMs ?? 320,
|
|
9517
|
-
postRollMs: engine.recordingPostRollMs ?? 320
|
|
9756
|
+
preRollMs: Math.max(TRANSCRIPTION_BOUNDARY_MARGIN_MS, engine.recordingPreRollMs ?? 320),
|
|
9757
|
+
postRollMs: Math.max(TRANSCRIPTION_BOUNDARY_MARGIN_MS, engine.recordingPostRollMs ?? 320)
|
|
9518
9758
|
};
|
|
9519
9759
|
const generation = engine.startGeneration;
|
|
9520
9760
|
const writer = await engine.recordings.begin({
|
|
@@ -9562,6 +9802,7 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9562
9802
|
onProcessingChange,
|
|
9563
9803
|
tail: Promise.resolve(),
|
|
9564
9804
|
pendingWrites: 0,
|
|
9805
|
+
pendingSpeechWrites: 0,
|
|
9565
9806
|
lang,
|
|
9566
9807
|
operation: uid(),
|
|
9567
9808
|
capturedSamples: 0,
|
|
@@ -9574,18 +9815,22 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9574
9815
|
};
|
|
9575
9816
|
engine.session = session;
|
|
9576
9817
|
const valid = () => engine.session === session && !signal?.aborted;
|
|
9818
|
+
const awaitingTail = [];
|
|
9577
9819
|
let stopping = false;
|
|
9820
|
+
let recoveryPending = false;
|
|
9578
9821
|
let captureFailure;
|
|
9579
9822
|
const captureValid = () => engine.session === session || stopping;
|
|
9580
9823
|
const notify2 = () => {
|
|
9581
9824
|
if (valid())
|
|
9582
9825
|
session.onProcessingChange?.({
|
|
9583
|
-
queued: session.transcriptionQueue.length,
|
|
9826
|
+
queued: awaitingTail.length + session.transcriptionQueue.length,
|
|
9584
9827
|
active: !!session.activeRequest,
|
|
9585
9828
|
// Only queued/running speech attempts belong in the visible transcription status.
|
|
9586
|
-
transcribing: session.transcriptionQueue.length + (session.activeRequest ? 1 : 0),
|
|
9587
|
-
//
|
|
9588
|
-
|
|
9829
|
+
transcribing: awaitingTail.length + session.transcriptionQueue.length + (session.activeRequest ? 1 : 0),
|
|
9830
|
+
// Speech-boundary bookkeeping must settle before delivery, but silent PCM writes must not reset it.
|
|
9831
|
+
deliveryPending: awaitingTail.length + session.transcriptionQueue.length + (session.activeRequest ? 1 : 0) + session.pendingSpeechWrites + (session.finishing || recoveryPending ? 1 : 0),
|
|
9832
|
+
// All writes still protect release, playback admission and page leave.
|
|
9833
|
+
pending: awaitingTail.length + session.transcriptionQueue.length + (session.activeRequest ? 1 : 0) + session.pendingWrites + (session.finishing || recoveryPending ? 1 : 0)
|
|
9589
9834
|
});
|
|
9590
9835
|
};
|
|
9591
9836
|
const failure = (error) => {
|
|
@@ -9594,14 +9839,16 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9594
9839
|
onError?.(new Error(engine.recordingErrorMessage(error)));
|
|
9595
9840
|
void engine.stop();
|
|
9596
9841
|
};
|
|
9597
|
-
const serialize = (task, preserve = false) => {
|
|
9842
|
+
const serialize = (task, preserve = false, speech = false) => {
|
|
9598
9843
|
session.pendingWrites++;
|
|
9844
|
+
if (speech) session.pendingSpeechWrites++;
|
|
9599
9845
|
notify2();
|
|
9600
9846
|
const work = session.tail.then(async () => {
|
|
9601
9847
|
if (preserve || valid()) await task();
|
|
9602
9848
|
});
|
|
9603
9849
|
session.tail = work.catch(failure).finally(() => {
|
|
9604
9850
|
session.pendingWrites--;
|
|
9851
|
+
if (speech) session.pendingSpeechWrites--;
|
|
9605
9852
|
notify2();
|
|
9606
9853
|
});
|
|
9607
9854
|
return work;
|
|
@@ -9621,19 +9868,20 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9621
9868
|
if (through - Math.max(liveSpeechStart, lastSplitQueued) < max) return;
|
|
9622
9869
|
const end = Math.max(liveSpeechStart, lastSplitQueued) + max;
|
|
9623
9870
|
lastSplitQueued = end;
|
|
9624
|
-
void serialize(
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9871
|
+
void serialize(
|
|
9872
|
+
async () => {
|
|
9873
|
+
const speech = [...writer.session.pins].reverse().find((p) => p.kind === "speech-start");
|
|
9874
|
+
if (speech && valid()) await queueSpeech(speech, end);
|
|
9875
|
+
},
|
|
9876
|
+
false,
|
|
9877
|
+
true
|
|
9878
|
+
).catch(() => {
|
|
9628
9879
|
});
|
|
9629
9880
|
};
|
|
9630
9881
|
const queueSpeech = async (pin2, end) => {
|
|
9631
9882
|
for (let at2 = Math.max(pin2.atSample, queuedThrough); at2 < end; at2 += max) {
|
|
9632
9883
|
const requestedStart = Math.max(0, at2 - policy.preRollMs * rate / 1e3);
|
|
9633
|
-
const requestedEnd = Math.min(
|
|
9634
|
-
writer.session.capturedSamples,
|
|
9635
|
-
Math.min(end, at2 + max) + policy.postRollMs * rate / 1e3
|
|
9636
|
-
);
|
|
9884
|
+
const requestedEnd = Math.min(end, at2 + max) + policy.postRollMs * rate / 1e3;
|
|
9637
9885
|
const attempt = {
|
|
9638
9886
|
id: uid(),
|
|
9639
9887
|
segmentId: pin2.id,
|
|
@@ -9645,13 +9893,26 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9645
9893
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9646
9894
|
status: "pending"
|
|
9647
9895
|
};
|
|
9896
|
+
awaitingTail.push(attempt);
|
|
9897
|
+
notify2();
|
|
9898
|
+
}
|
|
9899
|
+
queuedThrough = Math.max(queuedThrough, end);
|
|
9900
|
+
await releaseCapturedRanges();
|
|
9901
|
+
};
|
|
9902
|
+
async function releaseCapturedRanges() {
|
|
9903
|
+
if (!valid()) return;
|
|
9904
|
+
const captured = writer.session.capturedSamples;
|
|
9905
|
+
while (awaitingTail.length && (session.finished || awaitingTail[0].endSample <= captured)) {
|
|
9906
|
+
const pending = awaitingTail[0];
|
|
9907
|
+
const attempt = { ...pending, endSample: Math.min(pending.endSample, captured) };
|
|
9648
9908
|
await writer.attempt(attempt);
|
|
9909
|
+
if (!valid()) return;
|
|
9910
|
+
awaitingTail.shift();
|
|
9649
9911
|
session.transcriptionQueue.push(attempt);
|
|
9650
9912
|
notify2();
|
|
9651
9913
|
}
|
|
9652
|
-
queuedThrough = Math.max(queuedThrough, end);
|
|
9653
9914
|
void drain();
|
|
9654
|
-
}
|
|
9915
|
+
}
|
|
9655
9916
|
const pin = (kind, at2, confirmed) => {
|
|
9656
9917
|
const startSample = Math.min(
|
|
9657
9918
|
session.capturedSamples,
|
|
@@ -9665,14 +9926,18 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9665
9926
|
liveSpeechStart = startSample;
|
|
9666
9927
|
lastSplitQueued = startSample;
|
|
9667
9928
|
} else liveSpeechStart = null;
|
|
9668
|
-
void serialize(
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9929
|
+
void serialize(
|
|
9930
|
+
async () => {
|
|
9931
|
+
const previous = writer.session.pins.at(-1);
|
|
9932
|
+
if (kind === "speech-start" && previous?.kind === "silence-start" && previous.atSample > 0 && startSample - previous.atSample >= paragraphSamples)
|
|
9933
|
+
paragraphStarts.add(startSample);
|
|
9934
|
+
await writer.pin(kind, startSample, confirmedSample);
|
|
9935
|
+
if (valid() && kind === "silence-start" && previous?.kind === "speech-start")
|
|
9936
|
+
await queueSpeech(previous, startSample);
|
|
9937
|
+
},
|
|
9938
|
+
true,
|
|
9939
|
+
true
|
|
9940
|
+
).catch(() => {
|
|
9676
9941
|
});
|
|
9677
9942
|
};
|
|
9678
9943
|
const detector = new PinDetector({
|
|
@@ -9714,7 +9979,8 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9714
9979
|
"content-type": "audio/wav",
|
|
9715
9980
|
"x-dlv-client-id": session.operation,
|
|
9716
9981
|
"x-dlv-operation-id": attempt.id,
|
|
9717
|
-
"x-dlv-language": lang
|
|
9982
|
+
"x-dlv-language": lang,
|
|
9983
|
+
...transcriptionContextHeader(engine.recognitionContext)
|
|
9718
9984
|
},
|
|
9719
9985
|
credentials: "same-origin",
|
|
9720
9986
|
body: capture.encode(pcm, rate),
|
|
@@ -9768,7 +10034,114 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9768
10034
|
return;
|
|
9769
10035
|
}
|
|
9770
10036
|
const copy = pcm.slice();
|
|
9771
|
-
void serialize(
|
|
10037
|
+
void serialize(async () => {
|
|
10038
|
+
await writer.append(copy);
|
|
10039
|
+
await releaseCapturedRanges();
|
|
10040
|
+
}, true).catch(() => {
|
|
10041
|
+
});
|
|
10042
|
+
};
|
|
10043
|
+
let vadGeneration = 0;
|
|
10044
|
+
let restartAttempts = 0;
|
|
10045
|
+
let recovering = null;
|
|
10046
|
+
let deferredFailure = null;
|
|
10047
|
+
const sileroValid = () => valid() && !stopping && !session.finished && !session.finishing;
|
|
10048
|
+
const releaseSilero = () => {
|
|
10049
|
+
++vadGeneration;
|
|
10050
|
+
const previousCapture = session.capture;
|
|
10051
|
+
const previousVad = session.vad;
|
|
10052
|
+
session.capture = null;
|
|
10053
|
+
session.vad = null;
|
|
10054
|
+
try {
|
|
10055
|
+
previousCapture?.release();
|
|
10056
|
+
} finally {
|
|
10057
|
+
previousVad?.release();
|
|
10058
|
+
}
|
|
10059
|
+
};
|
|
10060
|
+
const terminalVadFailure = (error) => {
|
|
10061
|
+
if (!valid() || stopping || session.finished) return;
|
|
10062
|
+
captureFailure = asError(error).message;
|
|
10063
|
+
onError?.(new Error(engine.vadErrorMessage(error)));
|
|
10064
|
+
void engine.stop();
|
|
10065
|
+
};
|
|
10066
|
+
const installSilero = async () => {
|
|
10067
|
+
const token = ++vadGeneration;
|
|
10068
|
+
let installing = true;
|
|
10069
|
+
let installationFailure;
|
|
10070
|
+
const failed = (error) => {
|
|
10071
|
+
if (token !== vadGeneration || !sileroValid()) return;
|
|
10072
|
+
if (installing) installationFailure = error;
|
|
10073
|
+
else recoverSilero(error);
|
|
10074
|
+
};
|
|
10075
|
+
const vad = engine.vadFactory({
|
|
10076
|
+
onProbability: ({ probability, pcm }) => {
|
|
10077
|
+
if (token !== vadGeneration || !captureValid() || session.finished) return;
|
|
10078
|
+
restartAttempts = 0;
|
|
10079
|
+
detector.frame(probability, pcm.length);
|
|
10080
|
+
checkLiveSplit();
|
|
10081
|
+
},
|
|
10082
|
+
onError: failed,
|
|
10083
|
+
onReset: () => failed(new Error("VAD analysis interrupted."))
|
|
10084
|
+
});
|
|
10085
|
+
session.vad = vad;
|
|
10086
|
+
try {
|
|
10087
|
+
const attached = await engine.vadCapture(
|
|
10088
|
+
engine.meter.context,
|
|
10089
|
+
engine.meter.source,
|
|
10090
|
+
vad,
|
|
10091
|
+
(pcm, sampleRate) => {
|
|
10092
|
+
if (token === vadGeneration) ingest(pcm, sampleRate);
|
|
10093
|
+
}
|
|
10094
|
+
);
|
|
10095
|
+
if (token !== vadGeneration || !sileroValid()) {
|
|
10096
|
+
attached.release();
|
|
10097
|
+
vad.release();
|
|
10098
|
+
throw Object.assign(new Error("Cancelled"), { name: "AbortError" });
|
|
10099
|
+
}
|
|
10100
|
+
session.capture = attached;
|
|
10101
|
+
if (installationFailure) throw installationFailure;
|
|
10102
|
+
} finally {
|
|
10103
|
+
installing = false;
|
|
10104
|
+
}
|
|
10105
|
+
};
|
|
10106
|
+
const retrySilero = async (initialError) => {
|
|
10107
|
+
let error = initialError;
|
|
10108
|
+
while (sileroValid()) {
|
|
10109
|
+
if (error) {
|
|
10110
|
+
releaseSilero();
|
|
10111
|
+
if (restartAttempts >= 2) throw error;
|
|
10112
|
+
restartAttempts++;
|
|
10113
|
+
detector.skipTo(Math.round(session.capturedSamples * 16e3 / rate));
|
|
10114
|
+
await Promise.resolve();
|
|
10115
|
+
}
|
|
10116
|
+
if (!sileroValid()) return;
|
|
10117
|
+
try {
|
|
10118
|
+
await installSilero();
|
|
10119
|
+
return;
|
|
10120
|
+
} catch (failure2) {
|
|
10121
|
+
if (!sileroValid() || asError(failure2).name === "AbortError") return;
|
|
10122
|
+
error = failure2;
|
|
10123
|
+
}
|
|
10124
|
+
}
|
|
10125
|
+
};
|
|
10126
|
+
const recoverSilero = (error) => {
|
|
10127
|
+
if (!sileroValid()) return;
|
|
10128
|
+
if (recovering) {
|
|
10129
|
+
++vadGeneration;
|
|
10130
|
+
deferredFailure = error;
|
|
10131
|
+
return;
|
|
10132
|
+
}
|
|
10133
|
+
++vadGeneration;
|
|
10134
|
+
recoveryPending = true;
|
|
10135
|
+
notify2();
|
|
10136
|
+
recovering = Promise.resolve().then(() => retrySilero(error)).catch(terminalVadFailure).finally(() => {
|
|
10137
|
+
recovering = null;
|
|
10138
|
+
const pending = deferredFailure;
|
|
10139
|
+
deferredFailure = null;
|
|
10140
|
+
if (pending) recoverSilero(pending);
|
|
10141
|
+
else {
|
|
10142
|
+
recoveryPending = false;
|
|
10143
|
+
notify2();
|
|
10144
|
+
}
|
|
9772
10145
|
});
|
|
9773
10146
|
};
|
|
9774
10147
|
session.finishRecording = () => {
|
|
@@ -9776,10 +10149,12 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9776
10149
|
session.finishing = true;
|
|
9777
10150
|
session.finishPromise = Promise.resolve().then(async () => {
|
|
9778
10151
|
try {
|
|
10152
|
+
await recovering;
|
|
9779
10153
|
await session.capture?.finish();
|
|
9780
10154
|
if (!valid()) return;
|
|
9781
10155
|
session.finished = true;
|
|
9782
10156
|
detector.finish();
|
|
10157
|
+
await serialize(releaseCapturedRanges);
|
|
9783
10158
|
await session.tail;
|
|
9784
10159
|
await writer.finish();
|
|
9785
10160
|
session.vad?.release();
|
|
@@ -9797,6 +10172,8 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9797
10172
|
};
|
|
9798
10173
|
session.stopRecording = async () => {
|
|
9799
10174
|
stopping = true;
|
|
10175
|
+
awaitingTail.length = 0;
|
|
10176
|
+
await recovering;
|
|
9800
10177
|
session.activeRequest?.abort();
|
|
9801
10178
|
if (session.finishing) session.capture?.release();
|
|
9802
10179
|
else {
|
|
@@ -9826,31 +10203,7 @@ async function startRecordingRecognition(engine, options, capture) {
|
|
|
9826
10203
|
signal?.addEventListener("abort", session.abort, { once: true });
|
|
9827
10204
|
try {
|
|
9828
10205
|
if (capture.silero) {
|
|
9829
|
-
|
|
9830
|
-
onProbability: ({ probability, pcm }) => {
|
|
9831
|
-
if (captureValid() && !session.finished) {
|
|
9832
|
-
detector.frame(probability, pcm.length);
|
|
9833
|
-
checkLiveSplit();
|
|
9834
|
-
}
|
|
9835
|
-
},
|
|
9836
|
-
onError: (error) => {
|
|
9837
|
-
captureFailure = error.message;
|
|
9838
|
-
if (valid()) onError?.(new Error(engine.vadErrorMessage(error)));
|
|
9839
|
-
void engine.stop();
|
|
9840
|
-
},
|
|
9841
|
-
onReset: () => {
|
|
9842
|
-
captureFailure = "VAD analysis interrupted.";
|
|
9843
|
-
if (valid())
|
|
9844
|
-
onError?.(new Error(engine.vadErrorMessage(new Error("VAD analysis interrupted."))));
|
|
9845
|
-
void engine.stop();
|
|
9846
|
-
}
|
|
9847
|
-
});
|
|
9848
|
-
session.capture = await engine.vadCapture(
|
|
9849
|
-
engine.meter.context,
|
|
9850
|
-
engine.meter.source,
|
|
9851
|
-
session.vad,
|
|
9852
|
-
ingest
|
|
9853
|
-
);
|
|
10206
|
+
await retrySilero();
|
|
9854
10207
|
} else {
|
|
9855
10208
|
const processor = engine.meter.context.createScriptProcessor(4096, 1, 1);
|
|
9856
10209
|
const gain = engine.meter.context.createGain();
|
|
@@ -9936,6 +10289,7 @@ var WhisperHttpRecognitionEngine = class {
|
|
|
9936
10289
|
voiceDetectionPreset = defaultSettings.voiceDetectionPreset,
|
|
9937
10290
|
voiceDetectionCustomSilenceMs = defaultSettings.voiceDetectionCustomSilenceMs,
|
|
9938
10291
|
voiceDetectionCustomParagraphMs = defaultSettings.voiceDetectionCustomParagraphMs,
|
|
10292
|
+
recognitionContext = "",
|
|
9939
10293
|
maxUtteranceSeconds = 60,
|
|
9940
10294
|
recordings,
|
|
9941
10295
|
inputSource = "microphone",
|
|
@@ -9962,6 +10316,7 @@ var WhisperHttpRecognitionEngine = class {
|
|
|
9962
10316
|
this.voiceDetectionPreset = voiceDetectionPreset;
|
|
9963
10317
|
this.voiceDetectionCustomSilenceMs = voiceDetectionCustomSilenceMs;
|
|
9964
10318
|
this.voiceDetectionCustomParagraphMs = voiceDetectionCustomParagraphMs;
|
|
10319
|
+
this.recognitionContext = recognitionContext;
|
|
9965
10320
|
this.maxUtteranceSeconds = maxUtteranceSeconds;
|
|
9966
10321
|
Object.assign(this, {
|
|
9967
10322
|
recordings: recordings ?? createRecordingRepository(),
|
|
@@ -10053,7 +10408,13 @@ var WhisperHttpRecognitionEngine = class {
|
|
|
10053
10408
|
this.session = null;
|
|
10054
10409
|
session.signal?.removeEventListener("abort", session.abort);
|
|
10055
10410
|
await session.stopRecording();
|
|
10056
|
-
session.onProcessingChange?.({
|
|
10411
|
+
session.onProcessingChange?.({
|
|
10412
|
+
queued: 0,
|
|
10413
|
+
active: false,
|
|
10414
|
+
transcribing: 0,
|
|
10415
|
+
deliveryPending: 0,
|
|
10416
|
+
pending: 0
|
|
10417
|
+
});
|
|
10057
10418
|
}
|
|
10058
10419
|
};
|
|
10059
10420
|
|
|
@@ -10532,8 +10893,8 @@ function createDeviceOptions(devices, kind, systemDefault, fallback) {
|
|
|
10532
10893
|
var import_react36 = __toESM(require("react"), 1);
|
|
10533
10894
|
|
|
10534
10895
|
// src/modules/settings/services/releases.ts
|
|
10535
|
-
var CURRENT_VERSION = "0.6.
|
|
10536
|
-
var TESTED_DSH_VERSION = "0.2.1-
|
|
10896
|
+
var CURRENT_VERSION = "0.6.2";
|
|
10897
|
+
var TESTED_DSH_VERSION = "0.2.1-rc.2";
|
|
10537
10898
|
var REPOSITORY_URL = "https://github.com/victorwads/dsh-live-voice";
|
|
10538
10899
|
var RELEASES_URL = `${REPOSITORY_URL}/releases`;
|
|
10539
10900
|
var LATEST_RELEASE_API_URL = "https://api.github.com/repos/victorwads/dsh-live-voice/releases/latest";
|
|
@@ -10761,7 +11122,6 @@ function VoiceModeSettings({
|
|
|
10761
11122
|
|
|
10762
11123
|
// src/modules/settings/sections/conversation/ConversationDelaySettings.tsx
|
|
10763
11124
|
var import_react40 = __toESM(require("react"), 1);
|
|
10764
|
-
var DELAYS = [0, 1, 2, 3, 4];
|
|
10765
11125
|
function ConversationDelaySettings({ settings, updateSettings }) {
|
|
10766
11126
|
const { scoped: commons } = useLanguage((ctx) => ctx.commons);
|
|
10767
11127
|
const { scoped: speak } = useLanguage((ctx) => ctx.speak);
|
|
@@ -10770,10 +11130,12 @@ function ConversationDelaySettings({ settings, updateSettings }) {
|
|
|
10770
11130
|
{
|
|
10771
11131
|
label: speak.responseDelay.label(),
|
|
10772
11132
|
hint: speak.responseDelay.help(),
|
|
10773
|
-
value: String(
|
|
10774
|
-
|
|
11133
|
+
value: String(
|
|
11134
|
+
settings.assistantSpeechDelaySeconds ?? defaultSettings.assistantSpeechDelaySeconds
|
|
11135
|
+
),
|
|
11136
|
+
options: assistantSpeechDelayOptions.map((seconds) => ({
|
|
10775
11137
|
value: String(seconds),
|
|
10776
|
-
label: seconds
|
|
11138
|
+
label: seconds < 1 ? commons.milliseconds({ milliseconds: seconds * 1e3 }) : seconds === 1.5 ? speak.responseDelay.oneAndHalf() : commons.seconds({ seconds })
|
|
10777
11139
|
})),
|
|
10778
11140
|
onChange: (event) => updateSettings({ assistantSpeechDelaySeconds: Number(event.target.value) })
|
|
10779
11141
|
}
|
|
@@ -11518,7 +11880,18 @@ function RecognitionEngineSettings({
|
|
|
11518
11880
|
settings,
|
|
11519
11881
|
updateSettings
|
|
11520
11882
|
}
|
|
11521
|
-
),
|
|
11883
|
+
), ["whisper-http", "qwen-http"].includes(engine) && /* @__PURE__ */ import_react46.default.createElement(SettingsSubcard, { title: commons.connection.title(), icon: "link" }, engine === "whisper-http" ? /* @__PURE__ */ import_react46.default.createElement(WhisperSettings, { controller }) : settings.engine !== "qwen-http" && /* @__PURE__ */ import_react46.default.createElement(QwenSettings, { controller }), /* @__PURE__ */ import_react46.default.createElement(
|
|
11884
|
+
TextAreaField,
|
|
11885
|
+
{
|
|
11886
|
+
label: recognition.context.label(),
|
|
11887
|
+
hint: recognition.context.hint(),
|
|
11888
|
+
placeholder: recognition.context.placeholder(),
|
|
11889
|
+
value: settings.recognitionContext || "",
|
|
11890
|
+
rows: 3,
|
|
11891
|
+
maxLength: TRANSCRIPTION_CONTEXT_MAX_LENGTH,
|
|
11892
|
+
onCommit: (recognitionContext) => updateSettings({ recognitionContext })
|
|
11893
|
+
}
|
|
11894
|
+
)));
|
|
11522
11895
|
}
|
|
11523
11896
|
|
|
11524
11897
|
// src/modules/settings/sections/recognition/RecognitionFilterSettings.tsx
|
|
@@ -13057,7 +13430,9 @@ function createPluginUpdate({
|
|
|
13057
13430
|
if (!result?.ok) return patch({ ...state, phase: "uncertain", problem: "uncertain" });
|
|
13058
13431
|
const value = result.value;
|
|
13059
13432
|
if (value.application === "failed" || value.application === "cancelled")
|
|
13060
|
-
return fail(
|
|
13433
|
+
return fail(
|
|
13434
|
+
value.error?.code === "incompatible-version" || value.packageResult?.incompatible?.length ? "incompatible" : value.pendingBuilds?.length || value.packageResult?.kind === "build-blocked" ? "scripts" : ["not-found", "no-matching-version"].includes(value.packageResult?.kind) ? "delayed" : "failed"
|
|
13435
|
+
);
|
|
13061
13436
|
if (value.bundle !== "dsh-live-voice" || value.version !== state.version || !["applied", "restart-required"].includes(value.application))
|
|
13062
13437
|
return patch({ ...state, phase: "uncertain", problem: "uncertain" });
|
|
13063
13438
|
patch({ ...state, phase: value.application === "restart-required" ? "restart" : "done" });
|
|
@@ -13270,7 +13645,7 @@ async function removeBrowserModel(key, storage = caches) {
|
|
|
13270
13645
|
}
|
|
13271
13646
|
|
|
13272
13647
|
// src/modules/recognition/local/assets.ts
|
|
13273
|
-
var browserAsrAssetBase = () => "/api/dsh-live-voice/asr/" + encodeURIComponent(true ? "0.6.
|
|
13648
|
+
var browserAsrAssetBase = () => "/api/dsh-live-voice/asr/" + encodeURIComponent(true ? "0.6.2" : "test") + "/";
|
|
13274
13649
|
|
|
13275
13650
|
// src/modules/recognition/local/runtimeIdentity.ts
|
|
13276
13651
|
var workerProtocol = "asr-worker-v3-ort-1.31.0-dev.20260914-8d85527a0";
|
|
@@ -13738,7 +14113,7 @@ var BrowserLocalRecognitionEngine = class extends WhisperHttpRecognitionEngine {
|
|
|
13738
14113
|
message;
|
|
13739
14114
|
release;
|
|
13740
14115
|
constructor({ manager, modelKey, device = "webgpu", message: message2, ...options }) {
|
|
13741
|
-
const margins = ((options.recordingPreRollMs ?? 320) + (options.recordingPostRollMs ?? 320)) / 1e3;
|
|
14116
|
+
const margins = (Math.max(TRANSCRIPTION_BOUNDARY_MARGIN_MS, options.recordingPreRollMs ?? 320) + Math.max(TRANSCRIPTION_BOUNDARY_MARGIN_MS, options.recordingPostRollMs ?? 320)) / 1e3;
|
|
13742
14117
|
super({
|
|
13743
14118
|
...options,
|
|
13744
14119
|
maxUtteranceSeconds: Math.max(
|
|
@@ -13980,6 +14355,15 @@ function apply(ctx) {
|
|
|
13980
14355
|
const settingsControllers = /* @__PURE__ */ new Set();
|
|
13981
14356
|
let disposed = false;
|
|
13982
14357
|
const recordings = createRecordingRepository();
|
|
14358
|
+
const recordingTimeline = () => {
|
|
14359
|
+
const append = recordings.timeline();
|
|
14360
|
+
return (kind) => {
|
|
14361
|
+
void append(kind).catch(() => {
|
|
14362
|
+
for (const entry of controllers.values())
|
|
14363
|
+
entry.controller.patch({ error: t("dsh-live-voice.recording.error.failed") });
|
|
14364
|
+
});
|
|
14365
|
+
};
|
|
14366
|
+
};
|
|
13983
14367
|
const localModels = new LocalModelManager();
|
|
13984
14368
|
void localModels.refresh();
|
|
13985
14369
|
const leaveGuard = createTranscriptionLeaveGuard(window);
|
|
@@ -14008,7 +14392,7 @@ function apply(ctx) {
|
|
|
14008
14392
|
},
|
|
14009
14393
|
updatePlaybackSettings: (next) => meetingEntry?.controller.updateSettings(next),
|
|
14010
14394
|
handleQuestionResult: (result) => meetingEntry?.controller.composer.handleQuestionResult?.(result),
|
|
14011
|
-
canAutoSend: () => !!meetingEntry && !meetingEntry.controller.getSnapshot().recognizing && !meetingEntry.controller.
|
|
14395
|
+
canAutoSend: () => !!meetingEntry && !meetingEntry.controller.getSnapshot().recognizing && !meetingEntry.controller.deliveryPending()
|
|
14012
14396
|
},
|
|
14013
14397
|
settings: () => preferences.getSnapshot(),
|
|
14014
14398
|
savePreferences: (controller, next) => savePreferences(controller, next),
|
|
@@ -14142,6 +14526,7 @@ function apply(ctx) {
|
|
|
14142
14526
|
"browserQwenModel",
|
|
14143
14527
|
"browserAsrDevice",
|
|
14144
14528
|
"recognitionLang",
|
|
14529
|
+
"recognitionContext",
|
|
14145
14530
|
"inputDeviceId",
|
|
14146
14531
|
"recognitionProcessLocally",
|
|
14147
14532
|
"recognitionAutoInstall",
|
|
@@ -14161,6 +14546,7 @@ function apply(ctx) {
|
|
|
14161
14546
|
const changesRecognition = (next) => Object.keys(next).some((key) => recognitionSettingKeys.has(key));
|
|
14162
14547
|
const recordingOptions = (settings, inputSource) => ({
|
|
14163
14548
|
recordings,
|
|
14549
|
+
recognitionContext: settings.recognitionContext,
|
|
14164
14550
|
inputSource,
|
|
14165
14551
|
sourceId: inputSource,
|
|
14166
14552
|
sourceLabel: inputSource === "shared" ? settings.sharedSourceName : settings.micSourceName,
|
|
@@ -14361,13 +14747,14 @@ function apply(ctx) {
|
|
|
14361
14747
|
const meter = new MicrophoneMeter();
|
|
14362
14748
|
const recognition = recognitionFor(settings, meter);
|
|
14363
14749
|
entry.controller = new VoiceCoordinator({
|
|
14750
|
+
recordingTimeline,
|
|
14364
14751
|
persistSettings: (next) => savePreferences(entry.controller, next),
|
|
14365
14752
|
recognition,
|
|
14366
14753
|
engines: { browser: engineBrowser, say: engineSay, "qwen-http": engineQwen },
|
|
14367
14754
|
meter,
|
|
14368
14755
|
composer: {
|
|
14369
14756
|
getDraft: () => entry.draft,
|
|
14370
|
-
canAutoSend: () => !meeting.
|
|
14757
|
+
canAutoSend: () => !meeting.input().deliveryPending() && !meeting.input().getSnapshot().recognizing,
|
|
14371
14758
|
appendFinal: (text, startedAt, paragraphBefore) => {
|
|
14372
14759
|
meeting.cancelDelivery();
|
|
14373
14760
|
if (meetingEntry === entry && (entry.meeting?.getSnapshot().shared.listening || entry.controller.getSnapshot().timestamps)) {
|
|
@@ -14764,6 +15151,7 @@ function apply(ctx) {
|
|
|
14764
15151
|
const meter = new MicrophoneMeter();
|
|
14765
15152
|
const recognition = recognitionFor(settings, meter);
|
|
14766
15153
|
const c = new VoiceCoordinator({
|
|
15154
|
+
recordingTimeline,
|
|
14767
15155
|
persistSettings: (next) => savePreferences(c, next),
|
|
14768
15156
|
recognition,
|
|
14769
15157
|
engines: {
|