kugelaudio 0.7.0 → 0.8.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/dist/index.js CHANGED
@@ -39,7 +39,8 @@ __export(index_exports, {
39
39
  classifyWsHandshakeError: () => classifyWsHandshakeError,
40
40
  createWavBlob: () => createWavBlob,
41
41
  createWavFile: () => createWavFile,
42
- decodePCM16: () => decodePCM16
42
+ decodePCM16: () => decodePCM16,
43
+ parseSessionUsage: () => parseSessionUsage
43
44
  });
44
45
  module.exports = __toCommonJS(index_exports);
45
46
 
@@ -429,6 +430,23 @@ function classifyWsHandshakeError(err) {
429
430
  return build(status, void 0, typeof e.message === "string" ? e.message : "");
430
431
  }
431
432
 
433
+ // src/types.ts
434
+ function parseSessionUsage(data) {
435
+ const raw = data.usage;
436
+ const source = raw && typeof raw === "object" ? raw : data;
437
+ const audioSeconds = typeof source.audio_seconds === "number" ? source.audio_seconds : typeof data.total_audio_seconds === "number" ? data.total_audio_seconds : void 0;
438
+ if (audioSeconds === void 0) return null;
439
+ const costCents = typeof source.cost_cents === "number" ? source.cost_cents : null;
440
+ return {
441
+ audioSeconds,
442
+ costCents,
443
+ currency: typeof source.currency === "string" ? source.currency : void 0,
444
+ characters: typeof source.characters === "number" ? source.characters : void 0,
445
+ modelId: typeof source.model_id === "string" ? source.model_id : void 0,
446
+ costAvailable: costCents !== null
447
+ };
448
+ }
449
+
432
450
  // src/utils.ts
433
451
  function base64ToArrayBuffer(base64) {
434
452
  if (typeof atob === "function") {
@@ -515,7 +533,7 @@ function getWebSocket() {
515
533
  // package.json
516
534
  var package_default = {
517
535
  name: "kugelaudio",
518
- version: "0.7.0",
536
+ version: "0.8.0",
519
537
  description: "Official JavaScript/TypeScript SDK for KugelAudio TTS API",
520
538
  main: "dist/index.js",
521
539
  module: "dist/index.mjs",
@@ -1031,7 +1049,8 @@ var TTSResource = class {
1031
1049
  durationMs: data.dur_ms,
1032
1050
  generationMs: data.gen_ms,
1033
1051
  rtf: data.rtf,
1034
- error: data.error
1052
+ error: data.error,
1053
+ usage: parseSessionUsage(data) ?? void 0
1035
1054
  };
1036
1055
  pending.callbacks.onFinal?.(stats);
1037
1056
  this.pendingRequests.delete(requestId);
@@ -1122,11 +1141,15 @@ var TTSResource = class {
1122
1141
  ...options.temperature !== void 0 && { temperature: options.temperature },
1123
1142
  max_new_tokens: options.maxNewTokens ?? 2048,
1124
1143
  sample_rate: options.sampleRate ?? 24e3,
1144
+ ...options.outputFormat && { output_format: options.outputFormat },
1125
1145
  normalize: options.normalize ?? true,
1126
1146
  ...options.language && { language: options.language },
1127
1147
  ...options.wordTimestamps && { word_timestamps: true },
1128
1148
  ...options.speed !== void 0 && { speed: options.speed },
1129
- ...options.projectId !== void 0 && { project_id: options.projectId }
1149
+ ...options.projectId !== void 0 && { project_id: options.projectId },
1150
+ // [] is meaningful (explicit opt-out) and must be sent; only
1151
+ // undefined (use the project default) is omitted.
1152
+ ...options.dictionaryIds !== void 0 && { dictionary_ids: options.dictionaryIds }
1130
1153
  }));
1131
1154
  });
1132
1155
  }
@@ -1147,11 +1170,15 @@ var TTSResource = class {
1147
1170
  cfg_scale: options.cfgScale ?? 2,
1148
1171
  max_new_tokens: options.maxNewTokens ?? 2048,
1149
1172
  sample_rate: options.sampleRate ?? 24e3,
1173
+ ...options.outputFormat && { output_format: options.outputFormat },
1150
1174
  normalize: options.normalize ?? true,
1151
1175
  ...options.language && { language: options.language },
1152
1176
  ...options.wordTimestamps && { word_timestamps: true },
1153
1177
  ...options.speed !== void 0 && { speed: options.speed },
1154
- ...options.projectId !== void 0 && { project_id: options.projectId }
1178
+ ...options.projectId !== void 0 && { project_id: options.projectId },
1179
+ // [] is meaningful (explicit opt-out) and must be sent; only
1180
+ // undefined (use the project default) is omitted.
1181
+ ...options.dictionaryIds !== void 0 && { dictionary_ids: options.dictionaryIds }
1155
1182
  }));
1156
1183
  };
1157
1184
  ws.onmessage = (event) => {
@@ -1173,7 +1200,8 @@ var TTSResource = class {
1173
1200
  durationMs: data.dur_ms,
1174
1201
  generationMs: data.gen_ms,
1175
1202
  rtf: data.rtf,
1176
- error: data.error
1203
+ error: data.error,
1204
+ usage: parseSessionUsage(data) ?? void 0
1177
1205
  };
1178
1206
  callbacks.onFinal?.(stats);
1179
1207
  ws.close();
@@ -1343,7 +1371,11 @@ var MultiContextSession = class {
1343
1371
  this.ws = null;
1344
1372
  this.callbacks = {};
1345
1373
  this.contexts = /* @__PURE__ */ new Set();
1374
+ /** Contexts a create message has been sent for (not yet necessarily
1375
+ * confirmed by the server via context_created). */
1376
+ this.requestedContexts = /* @__PURE__ */ new Set();
1346
1377
  this._sessionId = null;
1378
+ this._contextUsage = /* @__PURE__ */ new Map();
1347
1379
  this.isStarted = false;
1348
1380
  this.config = config || {};
1349
1381
  }
@@ -1353,6 +1385,18 @@ var MultiContextSession = class {
1353
1385
  get sessionId() {
1354
1386
  return this._sessionId;
1355
1387
  }
1388
+ /**
1389
+ * Per-context usage (audio time + amount charged) for a closed context, or
1390
+ * null if that context hasn't closed yet. Each context is its own
1391
+ * conversation — use this to bill per conversation. See {@link SessionUsage}.
1392
+ */
1393
+ usageFor(contextId) {
1394
+ return this._contextUsage.get(contextId) ?? null;
1395
+ }
1396
+ /** Map of context_id → per-context usage for all closed contexts. */
1397
+ get contextUsage() {
1398
+ return new Map(this._contextUsage);
1399
+ }
1356
1400
  /**
1357
1401
  * Connect to the multi-context WebSocket endpoint.
1358
1402
  *
@@ -1406,12 +1450,19 @@ var MultiContextSession = class {
1406
1450
  };
1407
1451
  this.callbacks.onChunk?.(chunk);
1408
1452
  }
1453
+ if (data.final && data.context_id) {
1454
+ this.callbacks.onFinal?.(data.context_id);
1455
+ }
1409
1456
  if (data.context_closed) {
1410
1457
  this.contexts.delete(data.context_id);
1411
- this.callbacks.onContextClosed?.(data.context_id);
1458
+ this.requestedContexts.delete(data.context_id);
1459
+ const ctxUsage = parseSessionUsage(data) ?? void 0;
1460
+ if (ctxUsage) this._contextUsage.set(data.context_id, ctxUsage);
1461
+ this.callbacks.onContextClosed?.(data.context_id, ctxUsage);
1412
1462
  }
1413
1463
  if (data.context_timeout) {
1414
1464
  this.contexts.delete(data.context_id);
1465
+ this.requestedContexts.delete(data.context_id);
1415
1466
  this.callbacks.onContextTimeout?.(data.context_id);
1416
1467
  }
1417
1468
  if (data.session_closed) {
@@ -1451,6 +1502,7 @@ var MultiContextSession = class {
1451
1502
  this.ws = null;
1452
1503
  this.isStarted = false;
1453
1504
  this.contexts.clear();
1505
+ this.requestedContexts.clear();
1454
1506
  };
1455
1507
  });
1456
1508
  }
@@ -1461,6 +1513,7 @@ var MultiContextSession = class {
1461
1513
  if (!this.ws || this.ws.readyState !== WS_OPEN) {
1462
1514
  throw new KugelAudioError("WebSocket not connected");
1463
1515
  }
1516
+ this.requestedContexts.add(contextId);
1464
1517
  const msg = {
1465
1518
  text: " ",
1466
1519
  context_id: contextId
@@ -1468,23 +1521,27 @@ var MultiContextSession = class {
1468
1521
  if (!this.isStarted) {
1469
1522
  warnIfNoLanguage(this.config.language, this.config.normalize);
1470
1523
  if (this.config.sampleRate) msg.sample_rate = this.config.sampleRate;
1524
+ if (this.config.outputFormat) msg.output_format = this.config.outputFormat;
1471
1525
  if (this.config.cfgScale) msg.cfg_scale = this.config.cfgScale;
1472
1526
  if (this.config.temperature !== void 0) msg.temperature = this.config.temperature;
1473
1527
  if (this.config.maxNewTokens) msg.max_new_tokens = this.config.maxNewTokens;
1474
1528
  if (this.config.normalize !== void 0) msg.normalize = this.config.normalize;
1475
1529
  if (this.config.language) msg.language = this.config.language;
1530
+ if (this.config.dictionaryIds !== void 0) msg.dictionary_ids = this.config.dictionaryIds;
1476
1531
  if (this.config.inactivityTimeout) msg.inactivity_timeout = this.config.inactivityTimeout;
1477
1532
  }
1533
+ const voiceSettings = {};
1478
1534
  const voiceId = options?.voiceId || this.config.defaultVoiceId;
1479
- if (voiceId) msg.voice_id = voiceId;
1535
+ if (voiceId) voiceSettings.voice_id = voiceId;
1480
1536
  if (options?.voiceSettings) {
1481
- msg.voice_settings = {
1482
- stability: options.voiceSettings.stability,
1483
- similarity_boost: options.voiceSettings.similarityBoost,
1484
- style: options.voiceSettings.style,
1485
- use_speaker_boost: options.voiceSettings.useSpeakerBoost,
1486
- speed: options.voiceSettings.speed
1487
- };
1537
+ voiceSettings.stability = options.voiceSettings.stability;
1538
+ voiceSettings.similarity_boost = options.voiceSettings.similarityBoost;
1539
+ voiceSettings.style = options.voiceSettings.style;
1540
+ voiceSettings.use_speaker_boost = options.voiceSettings.useSpeakerBoost;
1541
+ voiceSettings.speed = options.voiceSettings.speed;
1542
+ }
1543
+ if (Object.keys(voiceSettings).length > 0) {
1544
+ msg.voice_settings = voiceSettings;
1488
1545
  }
1489
1546
  this.ws.send(JSON.stringify(msg));
1490
1547
  }
@@ -1495,7 +1552,7 @@ var MultiContextSession = class {
1495
1552
  if (!this.ws || this.ws.readyState !== WS_OPEN) {
1496
1553
  throw new KugelAudioError("WebSocket not connected");
1497
1554
  }
1498
- if (!this.contexts.has(contextId) && !this.isStarted) {
1555
+ if (!this.requestedContexts.has(contextId) && !this.contexts.has(contextId)) {
1499
1556
  this.createContext(contextId);
1500
1557
  }
1501
1558
  this.ws.send(JSON.stringify({
@@ -1553,6 +1610,7 @@ var MultiContextSession = class {
1553
1610
  this.ws = null;
1554
1611
  this.isStarted = false;
1555
1612
  this.contexts.clear();
1613
+ this.requestedContexts.clear();
1556
1614
  }
1557
1615
  /**
1558
1616
  * Get active context IDs.
@@ -1571,10 +1629,19 @@ var StreamingSession = class {
1571
1629
  constructor(client, config, callbacks) {
1572
1630
  this.ws = null;
1573
1631
  this.configSent = false;
1632
+ this._lastUsage = null;
1574
1633
  this.client = client;
1575
1634
  this.config = config;
1576
1635
  this.callbacks = callbacks;
1577
1636
  }
1637
+ /**
1638
+ * Per-session usage from the most recently closed session, or null before
1639
+ * the first session closes. Use this to bill your own customers per
1640
+ * conversation. See {@link SessionUsage}.
1641
+ */
1642
+ get lastUsage() {
1643
+ return this._lastUsage;
1644
+ }
1578
1645
  /**
1579
1646
  * Open the WebSocket connection and authenticate.
1580
1647
  *
@@ -1638,7 +1705,15 @@ var StreamingSession = class {
1638
1705
  if (data.interrupted) {
1639
1706
  this.callbacks.onInterrupted?.();
1640
1707
  }
1708
+ if (data.final) {
1709
+ this.callbacks.onFinal?.(
1710
+ data.total_audio_seconds ?? 0,
1711
+ data.total_text_chunks ?? 0,
1712
+ data.total_audio_chunks ?? 0
1713
+ );
1714
+ }
1641
1715
  if (data.session_closed) {
1716
+ this._lastUsage = parseSessionUsage(data);
1642
1717
  this.callbacks.onSessionClosed?.(
1643
1718
  data.total_audio_seconds ?? 0,
1644
1719
  data.total_text_chunks ?? 0,
@@ -1706,6 +1781,7 @@ var StreamingSession = class {
1706
1781
  if (this.config.temperature !== void 0) msg.temperature = this.config.temperature;
1707
1782
  if (this.config.maxNewTokens !== void 0) msg.max_new_tokens = this.config.maxNewTokens;
1708
1783
  if (this.config.sampleRate !== void 0) msg.sample_rate = this.config.sampleRate;
1784
+ if (this.config.outputFormat !== void 0) msg.output_format = this.config.outputFormat;
1709
1785
  if (this.config.flushTimeoutMs !== void 0) msg.flush_timeout_ms = this.config.flushTimeoutMs;
1710
1786
  if (this.config.maxBufferLength !== void 0) msg.max_buffer_length = this.config.maxBufferLength;
1711
1787
  if (this.config.normalize !== void 0) msg.normalize = this.config.normalize;
@@ -1714,6 +1790,7 @@ var StreamingSession = class {
1714
1790
  if (this.config.autoMode !== void 0) msg.auto_mode = this.config.autoMode;
1715
1791
  if (this.config.chunkLengthSchedule?.length) msg.chunk_length_schedule = this.config.chunkLengthSchedule;
1716
1792
  if (this.config.speed !== void 0) msg.speed = this.config.speed;
1793
+ if (this.config.dictionaryIds !== void 0) msg.dictionary_ids = this.config.dictionaryIds;
1717
1794
  this.configSent = true;
1718
1795
  }
1719
1796
  this.ws.send(JSON.stringify(msg));
@@ -2093,5 +2170,6 @@ var KugelAudio = class _KugelAudio {
2093
2170
  classifyWsHandshakeError,
2094
2171
  createWavBlob,
2095
2172
  createWavFile,
2096
- decodePCM16
2173
+ decodePCM16,
2174
+ parseSessionUsage
2097
2175
  });
package/dist/index.mjs CHANGED
@@ -391,6 +391,23 @@ function classifyWsHandshakeError(err) {
391
391
  return build(status, void 0, typeof e.message === "string" ? e.message : "");
392
392
  }
393
393
 
394
+ // src/types.ts
395
+ function parseSessionUsage(data) {
396
+ const raw = data.usage;
397
+ const source = raw && typeof raw === "object" ? raw : data;
398
+ const audioSeconds = typeof source.audio_seconds === "number" ? source.audio_seconds : typeof data.total_audio_seconds === "number" ? data.total_audio_seconds : void 0;
399
+ if (audioSeconds === void 0) return null;
400
+ const costCents = typeof source.cost_cents === "number" ? source.cost_cents : null;
401
+ return {
402
+ audioSeconds,
403
+ costCents,
404
+ currency: typeof source.currency === "string" ? source.currency : void 0,
405
+ characters: typeof source.characters === "number" ? source.characters : void 0,
406
+ modelId: typeof source.model_id === "string" ? source.model_id : void 0,
407
+ costAvailable: costCents !== null
408
+ };
409
+ }
410
+
394
411
  // src/utils.ts
395
412
  function base64ToArrayBuffer(base64) {
396
413
  if (typeof atob === "function") {
@@ -477,7 +494,7 @@ function getWebSocket() {
477
494
  // package.json
478
495
  var package_default = {
479
496
  name: "kugelaudio",
480
- version: "0.7.0",
497
+ version: "0.8.0",
481
498
  description: "Official JavaScript/TypeScript SDK for KugelAudio TTS API",
482
499
  main: "dist/index.js",
483
500
  module: "dist/index.mjs",
@@ -993,7 +1010,8 @@ var TTSResource = class {
993
1010
  durationMs: data.dur_ms,
994
1011
  generationMs: data.gen_ms,
995
1012
  rtf: data.rtf,
996
- error: data.error
1013
+ error: data.error,
1014
+ usage: parseSessionUsage(data) ?? void 0
997
1015
  };
998
1016
  pending.callbacks.onFinal?.(stats);
999
1017
  this.pendingRequests.delete(requestId);
@@ -1084,11 +1102,15 @@ var TTSResource = class {
1084
1102
  ...options.temperature !== void 0 && { temperature: options.temperature },
1085
1103
  max_new_tokens: options.maxNewTokens ?? 2048,
1086
1104
  sample_rate: options.sampleRate ?? 24e3,
1105
+ ...options.outputFormat && { output_format: options.outputFormat },
1087
1106
  normalize: options.normalize ?? true,
1088
1107
  ...options.language && { language: options.language },
1089
1108
  ...options.wordTimestamps && { word_timestamps: true },
1090
1109
  ...options.speed !== void 0 && { speed: options.speed },
1091
- ...options.projectId !== void 0 && { project_id: options.projectId }
1110
+ ...options.projectId !== void 0 && { project_id: options.projectId },
1111
+ // [] is meaningful (explicit opt-out) and must be sent; only
1112
+ // undefined (use the project default) is omitted.
1113
+ ...options.dictionaryIds !== void 0 && { dictionary_ids: options.dictionaryIds }
1092
1114
  }));
1093
1115
  });
1094
1116
  }
@@ -1109,11 +1131,15 @@ var TTSResource = class {
1109
1131
  cfg_scale: options.cfgScale ?? 2,
1110
1132
  max_new_tokens: options.maxNewTokens ?? 2048,
1111
1133
  sample_rate: options.sampleRate ?? 24e3,
1134
+ ...options.outputFormat && { output_format: options.outputFormat },
1112
1135
  normalize: options.normalize ?? true,
1113
1136
  ...options.language && { language: options.language },
1114
1137
  ...options.wordTimestamps && { word_timestamps: true },
1115
1138
  ...options.speed !== void 0 && { speed: options.speed },
1116
- ...options.projectId !== void 0 && { project_id: options.projectId }
1139
+ ...options.projectId !== void 0 && { project_id: options.projectId },
1140
+ // [] is meaningful (explicit opt-out) and must be sent; only
1141
+ // undefined (use the project default) is omitted.
1142
+ ...options.dictionaryIds !== void 0 && { dictionary_ids: options.dictionaryIds }
1117
1143
  }));
1118
1144
  };
1119
1145
  ws.onmessage = (event) => {
@@ -1135,7 +1161,8 @@ var TTSResource = class {
1135
1161
  durationMs: data.dur_ms,
1136
1162
  generationMs: data.gen_ms,
1137
1163
  rtf: data.rtf,
1138
- error: data.error
1164
+ error: data.error,
1165
+ usage: parseSessionUsage(data) ?? void 0
1139
1166
  };
1140
1167
  callbacks.onFinal?.(stats);
1141
1168
  ws.close();
@@ -1305,7 +1332,11 @@ var MultiContextSession = class {
1305
1332
  this.ws = null;
1306
1333
  this.callbacks = {};
1307
1334
  this.contexts = /* @__PURE__ */ new Set();
1335
+ /** Contexts a create message has been sent for (not yet necessarily
1336
+ * confirmed by the server via context_created). */
1337
+ this.requestedContexts = /* @__PURE__ */ new Set();
1308
1338
  this._sessionId = null;
1339
+ this._contextUsage = /* @__PURE__ */ new Map();
1309
1340
  this.isStarted = false;
1310
1341
  this.config = config || {};
1311
1342
  }
@@ -1315,6 +1346,18 @@ var MultiContextSession = class {
1315
1346
  get sessionId() {
1316
1347
  return this._sessionId;
1317
1348
  }
1349
+ /**
1350
+ * Per-context usage (audio time + amount charged) for a closed context, or
1351
+ * null if that context hasn't closed yet. Each context is its own
1352
+ * conversation — use this to bill per conversation. See {@link SessionUsage}.
1353
+ */
1354
+ usageFor(contextId) {
1355
+ return this._contextUsage.get(contextId) ?? null;
1356
+ }
1357
+ /** Map of context_id → per-context usage for all closed contexts. */
1358
+ get contextUsage() {
1359
+ return new Map(this._contextUsage);
1360
+ }
1318
1361
  /**
1319
1362
  * Connect to the multi-context WebSocket endpoint.
1320
1363
  *
@@ -1368,12 +1411,19 @@ var MultiContextSession = class {
1368
1411
  };
1369
1412
  this.callbacks.onChunk?.(chunk);
1370
1413
  }
1414
+ if (data.final && data.context_id) {
1415
+ this.callbacks.onFinal?.(data.context_id);
1416
+ }
1371
1417
  if (data.context_closed) {
1372
1418
  this.contexts.delete(data.context_id);
1373
- this.callbacks.onContextClosed?.(data.context_id);
1419
+ this.requestedContexts.delete(data.context_id);
1420
+ const ctxUsage = parseSessionUsage(data) ?? void 0;
1421
+ if (ctxUsage) this._contextUsage.set(data.context_id, ctxUsage);
1422
+ this.callbacks.onContextClosed?.(data.context_id, ctxUsage);
1374
1423
  }
1375
1424
  if (data.context_timeout) {
1376
1425
  this.contexts.delete(data.context_id);
1426
+ this.requestedContexts.delete(data.context_id);
1377
1427
  this.callbacks.onContextTimeout?.(data.context_id);
1378
1428
  }
1379
1429
  if (data.session_closed) {
@@ -1413,6 +1463,7 @@ var MultiContextSession = class {
1413
1463
  this.ws = null;
1414
1464
  this.isStarted = false;
1415
1465
  this.contexts.clear();
1466
+ this.requestedContexts.clear();
1416
1467
  };
1417
1468
  });
1418
1469
  }
@@ -1423,6 +1474,7 @@ var MultiContextSession = class {
1423
1474
  if (!this.ws || this.ws.readyState !== WS_OPEN) {
1424
1475
  throw new KugelAudioError("WebSocket not connected");
1425
1476
  }
1477
+ this.requestedContexts.add(contextId);
1426
1478
  const msg = {
1427
1479
  text: " ",
1428
1480
  context_id: contextId
@@ -1430,23 +1482,27 @@ var MultiContextSession = class {
1430
1482
  if (!this.isStarted) {
1431
1483
  warnIfNoLanguage(this.config.language, this.config.normalize);
1432
1484
  if (this.config.sampleRate) msg.sample_rate = this.config.sampleRate;
1485
+ if (this.config.outputFormat) msg.output_format = this.config.outputFormat;
1433
1486
  if (this.config.cfgScale) msg.cfg_scale = this.config.cfgScale;
1434
1487
  if (this.config.temperature !== void 0) msg.temperature = this.config.temperature;
1435
1488
  if (this.config.maxNewTokens) msg.max_new_tokens = this.config.maxNewTokens;
1436
1489
  if (this.config.normalize !== void 0) msg.normalize = this.config.normalize;
1437
1490
  if (this.config.language) msg.language = this.config.language;
1491
+ if (this.config.dictionaryIds !== void 0) msg.dictionary_ids = this.config.dictionaryIds;
1438
1492
  if (this.config.inactivityTimeout) msg.inactivity_timeout = this.config.inactivityTimeout;
1439
1493
  }
1494
+ const voiceSettings = {};
1440
1495
  const voiceId = options?.voiceId || this.config.defaultVoiceId;
1441
- if (voiceId) msg.voice_id = voiceId;
1496
+ if (voiceId) voiceSettings.voice_id = voiceId;
1442
1497
  if (options?.voiceSettings) {
1443
- msg.voice_settings = {
1444
- stability: options.voiceSettings.stability,
1445
- similarity_boost: options.voiceSettings.similarityBoost,
1446
- style: options.voiceSettings.style,
1447
- use_speaker_boost: options.voiceSettings.useSpeakerBoost,
1448
- speed: options.voiceSettings.speed
1449
- };
1498
+ voiceSettings.stability = options.voiceSettings.stability;
1499
+ voiceSettings.similarity_boost = options.voiceSettings.similarityBoost;
1500
+ voiceSettings.style = options.voiceSettings.style;
1501
+ voiceSettings.use_speaker_boost = options.voiceSettings.useSpeakerBoost;
1502
+ voiceSettings.speed = options.voiceSettings.speed;
1503
+ }
1504
+ if (Object.keys(voiceSettings).length > 0) {
1505
+ msg.voice_settings = voiceSettings;
1450
1506
  }
1451
1507
  this.ws.send(JSON.stringify(msg));
1452
1508
  }
@@ -1457,7 +1513,7 @@ var MultiContextSession = class {
1457
1513
  if (!this.ws || this.ws.readyState !== WS_OPEN) {
1458
1514
  throw new KugelAudioError("WebSocket not connected");
1459
1515
  }
1460
- if (!this.contexts.has(contextId) && !this.isStarted) {
1516
+ if (!this.requestedContexts.has(contextId) && !this.contexts.has(contextId)) {
1461
1517
  this.createContext(contextId);
1462
1518
  }
1463
1519
  this.ws.send(JSON.stringify({
@@ -1515,6 +1571,7 @@ var MultiContextSession = class {
1515
1571
  this.ws = null;
1516
1572
  this.isStarted = false;
1517
1573
  this.contexts.clear();
1574
+ this.requestedContexts.clear();
1518
1575
  }
1519
1576
  /**
1520
1577
  * Get active context IDs.
@@ -1533,10 +1590,19 @@ var StreamingSession = class {
1533
1590
  constructor(client, config, callbacks) {
1534
1591
  this.ws = null;
1535
1592
  this.configSent = false;
1593
+ this._lastUsage = null;
1536
1594
  this.client = client;
1537
1595
  this.config = config;
1538
1596
  this.callbacks = callbacks;
1539
1597
  }
1598
+ /**
1599
+ * Per-session usage from the most recently closed session, or null before
1600
+ * the first session closes. Use this to bill your own customers per
1601
+ * conversation. See {@link SessionUsage}.
1602
+ */
1603
+ get lastUsage() {
1604
+ return this._lastUsage;
1605
+ }
1540
1606
  /**
1541
1607
  * Open the WebSocket connection and authenticate.
1542
1608
  *
@@ -1600,7 +1666,15 @@ var StreamingSession = class {
1600
1666
  if (data.interrupted) {
1601
1667
  this.callbacks.onInterrupted?.();
1602
1668
  }
1669
+ if (data.final) {
1670
+ this.callbacks.onFinal?.(
1671
+ data.total_audio_seconds ?? 0,
1672
+ data.total_text_chunks ?? 0,
1673
+ data.total_audio_chunks ?? 0
1674
+ );
1675
+ }
1603
1676
  if (data.session_closed) {
1677
+ this._lastUsage = parseSessionUsage(data);
1604
1678
  this.callbacks.onSessionClosed?.(
1605
1679
  data.total_audio_seconds ?? 0,
1606
1680
  data.total_text_chunks ?? 0,
@@ -1668,6 +1742,7 @@ var StreamingSession = class {
1668
1742
  if (this.config.temperature !== void 0) msg.temperature = this.config.temperature;
1669
1743
  if (this.config.maxNewTokens !== void 0) msg.max_new_tokens = this.config.maxNewTokens;
1670
1744
  if (this.config.sampleRate !== void 0) msg.sample_rate = this.config.sampleRate;
1745
+ if (this.config.outputFormat !== void 0) msg.output_format = this.config.outputFormat;
1671
1746
  if (this.config.flushTimeoutMs !== void 0) msg.flush_timeout_ms = this.config.flushTimeoutMs;
1672
1747
  if (this.config.maxBufferLength !== void 0) msg.max_buffer_length = this.config.maxBufferLength;
1673
1748
  if (this.config.normalize !== void 0) msg.normalize = this.config.normalize;
@@ -1676,6 +1751,7 @@ var StreamingSession = class {
1676
1751
  if (this.config.autoMode !== void 0) msg.auto_mode = this.config.autoMode;
1677
1752
  if (this.config.chunkLengthSchedule?.length) msg.chunk_length_schedule = this.config.chunkLengthSchedule;
1678
1753
  if (this.config.speed !== void 0) msg.speed = this.config.speed;
1754
+ if (this.config.dictionaryIds !== void 0) msg.dictionary_ids = this.config.dictionaryIds;
1679
1755
  this.configSent = true;
1680
1756
  }
1681
1757
  this.ws.send(JSON.stringify(msg));
@@ -2054,5 +2130,6 @@ export {
2054
2130
  classifyWsHandshakeError,
2055
2131
  createWavBlob,
2056
2132
  createWavFile,
2057
- decodePCM16
2133
+ decodePCM16,
2134
+ parseSessionUsage
2058
2135
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kugelaudio",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Official JavaScript/TypeScript SDK for KugelAudio TTS API",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",