@absolutejs/voice 0.0.22-beta.573 → 0.0.22-beta.574

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/dist/index.js CHANGED
@@ -3870,6 +3870,7 @@ var createVoiceSession = (options) => {
3870
3870
  let adapterGenerationCounter = 0;
3871
3871
  let activeAdapterGeneration = 0;
3872
3872
  let activeTTSTurnId;
3873
+ let assistantSpeechEndsAt = 0;
3873
3874
  let fillerTimer = null;
3874
3875
  let fillerActive = false;
3875
3876
  let fillerToken = 0;
@@ -4262,6 +4263,7 @@ var createVoiceSession = (options) => {
4262
4263
  return;
4263
4264
  }
4264
4265
  activeTTSTurnId = undefined;
4266
+ assistantSpeechEndsAt = Date.now();
4265
4267
  appendTurnLatencyStage({
4266
4268
  metadata: { reason },
4267
4269
  stage: "tts_canceled",
@@ -4304,6 +4306,12 @@ var createVoiceSession = (options) => {
4304
4306
  turnId: activeTTSTurnId,
4305
4307
  type: "audio"
4306
4308
  });
4309
+ const bytesPerSample = input.format.encoding === "pcm_s16le" ? 2 : 1;
4310
+ const bytesPerSecond = input.format.sampleRateHz * input.format.channels * bytesPerSample;
4311
+ if (bytesPerSecond > 0) {
4312
+ const chunkMs = normalizedChunk.byteLength / bytesPerSecond * 1000;
4313
+ assistantSpeechEndsAt = Math.max(assistantSpeechEndsAt, Date.now()) + chunkMs;
4314
+ }
4307
4315
  if (activeTTSTurnId) {
4308
4316
  await appendTurnLatencyStage({
4309
4317
  at: input.receivedAt,
@@ -4413,6 +4421,20 @@ var createVoiceSession = (options) => {
4413
4421
  session
4414
4422
  });
4415
4423
  };
4424
+ const DRAIN_POLL_MS = 200;
4425
+ const DRAIN_TAIL_BUFFER_MS = 300;
4426
+ const DRAIN_MAX_MS = 12000;
4427
+ const drainAssistantSpeech = async () => {
4428
+ const startedAt = Date.now();
4429
+ while (Date.now() - startedAt < DRAIN_MAX_MS) {
4430
+ const remaining = assistantSpeechEndsAt + DRAIN_TAIL_BUFFER_MS - Date.now();
4431
+ if (remaining <= 0)
4432
+ return;
4433
+ await new Promise((resolve) => {
4434
+ setTimeout(resolve, Math.min(remaining, DRAIN_POLL_MS));
4435
+ });
4436
+ }
4437
+ };
4416
4438
  const completeInternal = async (result, input = {}) => {
4417
4439
  clearSilenceTimer();
4418
4440
  const disposition = input.disposition ?? "completed";
@@ -4446,6 +4468,9 @@ var createVoiceSession = (options) => {
4446
4468
  if (!didComplete) {
4447
4469
  return;
4448
4470
  }
4471
+ if (disposition === "completed") {
4472
+ await drainAssistantSpeech();
4473
+ }
4449
4474
  await appendTrace({
4450
4475
  payload: {
4451
4476
  disposition,
@@ -5786,6 +5786,7 @@ var createVoiceSession = (options) => {
5786
5786
  let adapterGenerationCounter = 0;
5787
5787
  let activeAdapterGeneration = 0;
5788
5788
  let activeTTSTurnId;
5789
+ let assistantSpeechEndsAt = 0;
5789
5790
  let fillerTimer = null;
5790
5791
  let fillerActive = false;
5791
5792
  let fillerToken = 0;
@@ -6178,6 +6179,7 @@ var createVoiceSession = (options) => {
6178
6179
  return;
6179
6180
  }
6180
6181
  activeTTSTurnId = undefined;
6182
+ assistantSpeechEndsAt = Date.now();
6181
6183
  appendTurnLatencyStage({
6182
6184
  metadata: { reason },
6183
6185
  stage: "tts_canceled",
@@ -6220,6 +6222,12 @@ var createVoiceSession = (options) => {
6220
6222
  turnId: activeTTSTurnId,
6221
6223
  type: "audio"
6222
6224
  });
6225
+ const bytesPerSample = input.format.encoding === "pcm_s16le" ? 2 : 1;
6226
+ const bytesPerSecond = input.format.sampleRateHz * input.format.channels * bytesPerSample;
6227
+ if (bytesPerSecond > 0) {
6228
+ const chunkMs = normalizedChunk.byteLength / bytesPerSecond * 1000;
6229
+ assistantSpeechEndsAt = Math.max(assistantSpeechEndsAt, Date.now()) + chunkMs;
6230
+ }
6223
6231
  if (activeTTSTurnId) {
6224
6232
  await appendTurnLatencyStage({
6225
6233
  at: input.receivedAt,
@@ -6329,6 +6337,20 @@ var createVoiceSession = (options) => {
6329
6337
  session
6330
6338
  });
6331
6339
  };
6340
+ const DRAIN_POLL_MS = 200;
6341
+ const DRAIN_TAIL_BUFFER_MS = 300;
6342
+ const DRAIN_MAX_MS = 12000;
6343
+ const drainAssistantSpeech = async () => {
6344
+ const startedAt = Date.now();
6345
+ while (Date.now() - startedAt < DRAIN_MAX_MS) {
6346
+ const remaining = assistantSpeechEndsAt + DRAIN_TAIL_BUFFER_MS - Date.now();
6347
+ if (remaining <= 0)
6348
+ return;
6349
+ await new Promise((resolve2) => {
6350
+ setTimeout(resolve2, Math.min(remaining, DRAIN_POLL_MS));
6351
+ });
6352
+ }
6353
+ };
6332
6354
  const completeInternal = async (result, input = {}) => {
6333
6355
  clearSilenceTimer();
6334
6356
  const disposition = input.disposition ?? "completed";
@@ -6362,6 +6384,9 @@ var createVoiceSession = (options) => {
6362
6384
  if (!didComplete) {
6363
6385
  return;
6364
6386
  }
6387
+ if (disposition === "completed") {
6388
+ await drainAssistantSpeech();
6389
+ }
6365
6390
  await appendTrace({
6366
6391
  payload: {
6367
6392
  disposition,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.573",
3
+ "version": "0.0.22-beta.574",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",