kugelaudio 0.7.0 → 0.9.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/CHANGELOG.md +21 -0
- package/README.md +41 -3
- package/dist/chunk-CB3B6T7F.mjs +391 -0
- package/dist/chunk-D57AKD2S.mjs +391 -0
- package/dist/index.d.mts +200 -16
- package/dist/index.d.ts +200 -16
- package/dist/index.js +287 -23
- package/dist/index.mjs +276 -347
- package/dist/livekit/index.d.mts +222 -0
- package/dist/livekit/index.d.ts +222 -0
- package/dist/livekit/index.js +994 -0
- package/dist/livekit/index.mjs +746 -0
- package/package.json +23 -3
- package/src/cfg-scale.test.ts +33 -0
- package/src/client.test.ts +466 -2
- package/src/client.ts +308 -17
- package/src/index.ts +4 -0
- package/src/livekit/index.ts +32 -0
- package/src/livekit/models.test.ts +30 -0
- package/src/livekit/models.ts +84 -0
- package/src/livekit/tts.functional.test.ts +348 -0
- package/src/livekit/tts.ts +833 -0
- package/src/livekit/wire.test.ts +112 -0
- package/src/livekit/wire.ts +126 -0
- package/src/types.ts +171 -13
- package/src/utils.ts +16 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
## [kugelaudio-v0.9.0](https://github.com/Kugelaudio/KugelAudio/compare/js-sdk-v0.8.0...js-sdk-v0.9.0) (2026-07-08)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
* **ingress:** per-span <prosody rate> speed control ([#1373](https://github.com/Kugelaudio/KugelAudio/issues/1373)) ([7861e88](https://github.com/Kugelaudio/KugelAudio/commit/7861e881cc9d89730f01681f72192ad7a6f3d2b8))
|
|
6
|
+
* **js-sdk:** add LiveKit Agents TTS plugin ([#1538](https://github.com/Kugelaudio/KugelAudio/issues/1538)) ([1b21c44](https://github.com/Kugelaudio/KugelAudio/commit/1b21c44b57ebfcd7cc19f9e1ea5c63e3aa4f4c71))
|
|
7
|
+
* update session settings per turn (KUG-1166) ([#1500](https://github.com/Kugelaudio/KugelAudio/issues/1500)) ([7521d35](https://github.com/Kugelaudio/KugelAudio/commit/7521d35d56925f2f76d6ac19f40bdd735680584c))
|
|
8
|
+
|
|
9
|
+
## [kugelaudio-v0.8.0](https://github.com/Kugelaudio/KugelAudio/compare/js-sdk-v0.7.0...js-sdk-v0.8.0) (2026-06-10)
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **ingress,python-sdk,js-sdk,java-sdk:** per-session usage over WebSocket ([#1346](https://github.com/Kugelaudio/KugelAudio/issues/1346)) ([2881881](https://github.com/Kugelaudio/KugelAudio/commit/28818816dca9c8d222391691d70f458c0eb28ed8))
|
|
14
|
+
* **ingress,python-sdk,js-sdk:** streaming final end-of-audio frame ([#1362](https://github.com/Kugelaudio/KugelAudio/issues/1362)) ([3fa95d2](https://github.com/Kugelaudio/KugelAudio/commit/3fa95d2f8597e6c9ced0aaf8370682dbcb123c71))
|
|
15
|
+
* **ingress:** output_format token + server-side G.711 (KUG-1190) ([#1345](https://github.com/Kugelaudio/KugelAudio/issues/1345)) ([3723291](https://github.com/Kugelaudio/KugelAudio/commit/372329196c4c91aa41fe2111783872874b6e895b))
|
|
16
|
+
* per-request dictionary selection (KUG-1094) ([#1361](https://github.com/Kugelaudio/KugelAudio/issues/1361)) ([3c28968](https://github.com/Kugelaudio/KugelAudio/commit/3c28968d32018bf3cafe1d312f32831668ea96b8))
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* **js-sdk,java-sdk,ingress:** multi-turn conversations work end-to-end + live SDK e2e bench in CI (KUG-1233) ([#1363](https://github.com/Kugelaudio/KugelAudio/issues/1363)) ([c0ed2a9](https://github.com/Kugelaudio/KugelAudio/commit/c0ed2a9cf41025bac5c7182c1a281eb600d8dd36))
|
|
21
|
+
|
|
1
22
|
## [kugelaudio-v0.7.0](https://github.com/Kugelaudio/KugelAudio/compare/js-sdk-v0.6.1...js-sdk-v0.7.0) (2026-06-06)
|
|
2
23
|
|
|
3
24
|
### Features
|
package/README.md
CHANGED
|
@@ -170,9 +170,10 @@ const audio = await client.tts.generate({
|
|
|
170
170
|
text: 'Hello, this is a test of the KugelAudio text-to-speech system.',
|
|
171
171
|
modelId: 'kugel-1-turbo', // 'kugel-1-turbo' (fast) or 'kugel-1' (quality)
|
|
172
172
|
voiceId: 123, // Optional: specific voice ID
|
|
173
|
-
cfgScale: 2.0, // Guidance scale (1.
|
|
173
|
+
cfgScale: 2.0, // Guidance scale (1.2-2.5)
|
|
174
174
|
maxNewTokens: 2048, // Maximum tokens to generate
|
|
175
175
|
sampleRate: 24000, // Output sample rate
|
|
176
|
+
outputFormat: undefined, // Optional: 'pcm_24000', 'ulaw_8000', 'alaw_8000', ...
|
|
176
177
|
normalize: true, // Enable text normalization (see below)
|
|
177
178
|
language: 'en', // Language for normalization
|
|
178
179
|
});
|
|
@@ -298,15 +299,51 @@ for await (const token of llmTokenStream) {
|
|
|
298
299
|
// Triggers the server-side final flush of any trailing text,
|
|
299
300
|
// streams the resulting audio through onChunk, then closes the WS.
|
|
300
301
|
await session.close();
|
|
302
|
+
|
|
303
|
+
// Per-session usage — bill your own customers per conversation.
|
|
304
|
+
// `costCents` is the actual charge in EUR cents (null if undetermined).
|
|
305
|
+
const usage = session.lastUsage;
|
|
306
|
+
if (usage) {
|
|
307
|
+
console.log(`audio: ${usage.audioSeconds}s, cost: ${usage.costCents ?? 'n/a'} ct`);
|
|
308
|
+
}
|
|
301
309
|
```
|
|
302
310
|
|
|
303
311
|
> ⚠️ **Do not call `session.send(text, true)` (`flush=true`) between
|
|
304
312
|
> sentences or words.** Each explicit flush is a separate TTS request
|
|
305
313
|
> that pays the full model time-to-first-audio (TTFA) again and produces
|
|
306
|
-
> an audible gap. See [
|
|
314
|
+
> an audible gap. See [Chunking & per-segment latency](https://docs.kugelaudio.com/streaming/chunking-and-latency)
|
|
307
315
|
> for the full rationale, chunk-size ordering, and ElevenLabs migration
|
|
308
316
|
> notes.
|
|
309
317
|
|
|
318
|
+
## LiveKit Agents Integration
|
|
319
|
+
|
|
320
|
+
Use KugelAudio as the TTS provider in a [LiveKit Agents for Node.js](https://github.com/livekit/agents-js) voice agent. Import from the `kugelaudio/livekit` subpath.
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
npm install kugelaudio @livekit/agents @livekit/rtc-node
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
`@livekit/agents` and `@livekit/rtc-node` are **optional peer dependencies** — only needed when you import `kugelaudio/livekit`.
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
import { TTS as KugelAudioTTS } from 'kugelaudio/livekit';
|
|
330
|
+
import { voice } from '@livekit/agents';
|
|
331
|
+
|
|
332
|
+
const tts = new KugelAudioTTS({
|
|
333
|
+
model: 'kugel-3',
|
|
334
|
+
voiceId: 1071,
|
|
335
|
+
language: 'en', // skip auto-detection for lower latency
|
|
336
|
+
sampleRate: 24000,
|
|
337
|
+
});
|
|
338
|
+
tts.prewarm(); // open the WebSocket ahead of the first response
|
|
339
|
+
|
|
340
|
+
const session = new voice.AgentSession({ /* stt, llm, vad, */ tts });
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
See [the LiveKit integration guide](https://docs.kugelaudio.com/integrations/livekit) and the runnable worker in `examples/livekit_agent.ts`.
|
|
344
|
+
|
|
345
|
+
> **Pipecat:** there is no JS/TS Pipecat integration — Pipecat's server framework is Python-only. Use the Python SDK's `kugelaudio.pipecat`, or LiveKit above for a server-side JS/TS agent.
|
|
346
|
+
|
|
310
347
|
## Text Normalization
|
|
311
348
|
|
|
312
349
|
Text normalization converts numbers, dates, times, and other non-verbal text into spoken words. For example:
|
|
@@ -413,6 +450,7 @@ interface GenerateOptions {
|
|
|
413
450
|
cfgScale?: number; // Default: 2.0
|
|
414
451
|
maxNewTokens?: number; // Default: 2048
|
|
415
452
|
sampleRate?: number; // Default: 24000
|
|
453
|
+
outputFormat?: string; // 'pcm_8000' | 'pcm_16000' | 'pcm_22050' | 'pcm_24000' | 'ulaw_8000' | 'alaw_8000'
|
|
416
454
|
normalize?: boolean; // Default: true - Enable text normalization
|
|
417
455
|
language?: string; // ISO 639-1 code for normalization (e.g., 'en', 'de')
|
|
418
456
|
}
|
|
@@ -425,7 +463,7 @@ interface GenerateOptions {
|
|
|
425
463
|
```typescript
|
|
426
464
|
interface AudioChunk {
|
|
427
465
|
audio: string; // Base64-encoded PCM16 audio
|
|
428
|
-
encoding: string; // 'pcm_s16le'
|
|
466
|
+
encoding: string; // 'pcm_s16le' | 'mulaw' | 'alaw' (G.711 when outputFormat set)
|
|
429
467
|
index: number; // Chunk index (0-based)
|
|
430
468
|
sampleRate: number; // Sample rate (24000)
|
|
431
469
|
samples: number; // Number of samples in chunk
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
var __typeError = (msg) => {
|
|
2
|
+
throw TypeError(msg);
|
|
3
|
+
};
|
|
4
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
+
}) : x)(function(x) {
|
|
7
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
+
});
|
|
10
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
11
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
12
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
13
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
14
|
+
var __privateMethod = (obj, member, method) => (__accessCheck(obj, member, "access private method"), method);
|
|
15
|
+
|
|
16
|
+
// src/errors.ts
|
|
17
|
+
var ErrorCodes = {
|
|
18
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
19
|
+
RATE_LIMITED: "RATE_LIMITED",
|
|
20
|
+
INSUFFICIENT_CREDITS: "INSUFFICIENT_CREDITS",
|
|
21
|
+
MODEL_UNAVAILABLE: "MODEL_UNAVAILABLE",
|
|
22
|
+
EMPTY_AUDIO: "EMPTY_AUDIO",
|
|
23
|
+
VALIDATION: "VALIDATION_ERROR",
|
|
24
|
+
INTERNAL: "INTERNAL_ERROR",
|
|
25
|
+
NOT_FOUND: "NOT_FOUND",
|
|
26
|
+
MISSING_VOICE_ID: "MISSING_VOICE_ID",
|
|
27
|
+
TOO_MANY_CONTEXTS: "TOO_MANY_CONTEXTS"
|
|
28
|
+
};
|
|
29
|
+
var WsCloseCodes = {
|
|
30
|
+
UNAUTHORIZED: 4001,
|
|
31
|
+
INSUFFICIENT_CREDITS: 4003,
|
|
32
|
+
RATE_LIMITED: 4029,
|
|
33
|
+
MODEL_UNAVAILABLE: 4500
|
|
34
|
+
};
|
|
35
|
+
var API_KEYS_URL = "https://app.kugelaudio.com/settings/api-keys";
|
|
36
|
+
var BILLING_URL = "https://app.kugelaudio.com/billing";
|
|
37
|
+
var KugelAudioError = class _KugelAudioError extends Error {
|
|
38
|
+
constructor(message, options = {}) {
|
|
39
|
+
super(options.requestId ? `${message} (request_id: ${options.requestId})` : message);
|
|
40
|
+
this.name = "KugelAudioError";
|
|
41
|
+
this.statusCode = options.statusCode;
|
|
42
|
+
this.errorCode = options.errorCode;
|
|
43
|
+
this.requestId = options.requestId;
|
|
44
|
+
this.retryAfter = options.retryAfter;
|
|
45
|
+
Object.setPrototypeOf(this, _KugelAudioError.prototype);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
var AuthenticationError = class _AuthenticationError extends KugelAudioError {
|
|
49
|
+
constructor(message, options = {}) {
|
|
50
|
+
super(
|
|
51
|
+
message ?? `KugelAudio rejected the API key. Check it is current at ${API_KEYS_URL}.`,
|
|
52
|
+
{ statusCode: 401, errorCode: ErrorCodes.UNAUTHORIZED, ...options }
|
|
53
|
+
);
|
|
54
|
+
this.name = "AuthenticationError";
|
|
55
|
+
Object.setPrototypeOf(this, _AuthenticationError.prototype);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
var RateLimitError = class _RateLimitError extends KugelAudioError {
|
|
59
|
+
constructor(message, options = {}) {
|
|
60
|
+
const msg = message ?? (options.retryAfter ? `KugelAudio rate limit hit; retry after ${options.retryAfter}s.` : "KugelAudio rate limit hit; retry shortly.");
|
|
61
|
+
super(msg, { statusCode: 429, errorCode: ErrorCodes.RATE_LIMITED, ...options });
|
|
62
|
+
this.name = "RateLimitError";
|
|
63
|
+
Object.setPrototypeOf(this, _RateLimitError.prototype);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
var InsufficientCreditsError = class _InsufficientCreditsError extends KugelAudioError {
|
|
67
|
+
constructor(message, options = {}) {
|
|
68
|
+
super(
|
|
69
|
+
message ?? `Your KugelAudio account is out of credits. Top up at ${BILLING_URL}.`,
|
|
70
|
+
{ statusCode: 402, errorCode: ErrorCodes.INSUFFICIENT_CREDITS, ...options }
|
|
71
|
+
);
|
|
72
|
+
this.name = "InsufficientCreditsError";
|
|
73
|
+
Object.setPrototypeOf(this, _InsufficientCreditsError.prototype);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
var ValidationError = class _ValidationError extends KugelAudioError {
|
|
77
|
+
constructor(message, options = {}) {
|
|
78
|
+
super(message, { statusCode: 400, errorCode: ErrorCodes.VALIDATION, ...options });
|
|
79
|
+
this.name = "ValidationError";
|
|
80
|
+
Object.setPrototypeOf(this, _ValidationError.prototype);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
var ConnectionError = class _ConnectionError extends KugelAudioError {
|
|
84
|
+
constructor(message, options = {}) {
|
|
85
|
+
super(message, { statusCode: 503, ...options });
|
|
86
|
+
this.name = "ConnectionError";
|
|
87
|
+
Object.setPrototypeOf(this, _ConnectionError.prototype);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
var NotFoundError = class _NotFoundError extends KugelAudioError {
|
|
91
|
+
constructor(message, options = {}) {
|
|
92
|
+
super(message ?? "Not found.", {
|
|
93
|
+
statusCode: 404,
|
|
94
|
+
errorCode: ErrorCodes.NOT_FOUND,
|
|
95
|
+
...options
|
|
96
|
+
});
|
|
97
|
+
this.name = "NotFoundError";
|
|
98
|
+
Object.setPrototypeOf(this, _NotFoundError.prototype);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
function build(status, errorCode, message, opts = {}) {
|
|
102
|
+
const common = { ...opts };
|
|
103
|
+
if (status !== void 0) common.statusCode = status;
|
|
104
|
+
if (errorCode !== void 0) common.errorCode = errorCode;
|
|
105
|
+
if (errorCode === ErrorCodes.UNAUTHORIZED || status === 401) {
|
|
106
|
+
return new AuthenticationError(message || void 0, common);
|
|
107
|
+
}
|
|
108
|
+
if (errorCode === ErrorCodes.INSUFFICIENT_CREDITS || status === 402) {
|
|
109
|
+
return new InsufficientCreditsError(message || void 0, common);
|
|
110
|
+
}
|
|
111
|
+
if (errorCode === ErrorCodes.RATE_LIMITED || errorCode === ErrorCodes.TOO_MANY_CONTEXTS || status === 429) {
|
|
112
|
+
return new RateLimitError(message || void 0, common);
|
|
113
|
+
}
|
|
114
|
+
if (errorCode === ErrorCodes.VALIDATION || errorCode === ErrorCodes.MISSING_VOICE_ID || status === 400) {
|
|
115
|
+
return new ValidationError(message || "Request validation failed.", common);
|
|
116
|
+
}
|
|
117
|
+
if (errorCode === ErrorCodes.MODEL_UNAVAILABLE || status === 503) {
|
|
118
|
+
const detail = message || "service temporarily unavailable";
|
|
119
|
+
return new ConnectionError(
|
|
120
|
+
`KugelAudio is temporarily unavailable: ${detail}. Retry shortly.`,
|
|
121
|
+
common
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
if (errorCode === ErrorCodes.NOT_FOUND || status === 404) {
|
|
125
|
+
return new NotFoundError(message || void 0, common);
|
|
126
|
+
}
|
|
127
|
+
return new KugelAudioError(message || `HTTP ${status}`, common);
|
|
128
|
+
}
|
|
129
|
+
function readHeader(headers, name) {
|
|
130
|
+
if (headers && typeof headers.get === "function") {
|
|
131
|
+
return headers.get(name) ?? void 0;
|
|
132
|
+
}
|
|
133
|
+
const rec = headers;
|
|
134
|
+
return rec[name] ?? rec[name.toLowerCase()] ?? void 0;
|
|
135
|
+
}
|
|
136
|
+
function classifyHttpError(status, bodyText, headers) {
|
|
137
|
+
let errorCode;
|
|
138
|
+
let message = "";
|
|
139
|
+
let retryAfter;
|
|
140
|
+
if (bodyText) {
|
|
141
|
+
try {
|
|
142
|
+
const body = JSON.parse(bodyText);
|
|
143
|
+
if (body && typeof body === "object") {
|
|
144
|
+
errorCode = typeof body.error_code === "string" ? body.error_code : void 0;
|
|
145
|
+
const msg = body.error ?? body.detail;
|
|
146
|
+
if (Array.isArray(msg)) {
|
|
147
|
+
message = msg.map((m) => String(m)).join("; ");
|
|
148
|
+
} else if (typeof msg === "string") {
|
|
149
|
+
message = msg;
|
|
150
|
+
}
|
|
151
|
+
if (typeof body.retry_after === "number") {
|
|
152
|
+
retryAfter = body.retry_after;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} catch {
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (retryAfter === void 0) {
|
|
159
|
+
const header = readHeader(headers, "Retry-After") ?? readHeader(headers, "retry-after");
|
|
160
|
+
if (header) {
|
|
161
|
+
const n = Number(header);
|
|
162
|
+
if (Number.isFinite(n)) retryAfter = n;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const requestId = readHeader(headers, "x-request-id") ?? readHeader(headers, "X-Request-Id");
|
|
166
|
+
if (!message) {
|
|
167
|
+
message = (bodyText || "").trim();
|
|
168
|
+
}
|
|
169
|
+
return build(status, errorCode, message, { requestId, retryAfter });
|
|
170
|
+
}
|
|
171
|
+
function classifyWsFrame(data) {
|
|
172
|
+
const errorCode = data.error_code;
|
|
173
|
+
const message = data.error ?? "Server reported an error.";
|
|
174
|
+
const status = typeof data.code === "number" ? data.code : void 0;
|
|
175
|
+
const retryAfter = typeof data.retry_after === "number" ? data.retry_after : void 0;
|
|
176
|
+
return build(status, errorCode, message, { retryAfter });
|
|
177
|
+
}
|
|
178
|
+
function classifyWsClose(code, reason) {
|
|
179
|
+
const reasonTxt = (reason ?? "").trim();
|
|
180
|
+
if (code === WsCloseCodes.UNAUTHORIZED) {
|
|
181
|
+
let msg = `KugelAudio rejected the API key. Check it is current at ${API_KEYS_URL}.`;
|
|
182
|
+
if (reasonTxt) msg = `${msg} (${reasonTxt})`;
|
|
183
|
+
return new AuthenticationError(msg);
|
|
184
|
+
}
|
|
185
|
+
if (code === WsCloseCodes.INSUFFICIENT_CREDITS) {
|
|
186
|
+
return new InsufficientCreditsError();
|
|
187
|
+
}
|
|
188
|
+
if (code === WsCloseCodes.RATE_LIMITED) {
|
|
189
|
+
return new RateLimitError();
|
|
190
|
+
}
|
|
191
|
+
if (code === WsCloseCodes.MODEL_UNAVAILABLE) {
|
|
192
|
+
const suffix = reasonTxt ? ` (${reasonTxt})` : "";
|
|
193
|
+
return new ConnectionError(
|
|
194
|
+
`KugelAudio model is temporarily unavailable. Retry shortly.${suffix}`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
const detail = reasonTxt || "no reason given";
|
|
198
|
+
const codeStr = code !== void 0 ? ` (code ${code})` : "";
|
|
199
|
+
return new ConnectionError(
|
|
200
|
+
`KugelAudio WebSocket closed by server: ${detail}${codeStr}.`
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
function classifyWsHandshakeError(err) {
|
|
204
|
+
if (!err || typeof err !== "object") return null;
|
|
205
|
+
const e = err;
|
|
206
|
+
let status;
|
|
207
|
+
if (typeof e.statusCode === "number") {
|
|
208
|
+
status = e.statusCode;
|
|
209
|
+
}
|
|
210
|
+
if (status === void 0 && typeof e.message === "string") {
|
|
211
|
+
const m = e.message.match(/Unexpected server response:\s*(\d{3})/i);
|
|
212
|
+
if (m) status = Number(m[1]);
|
|
213
|
+
}
|
|
214
|
+
if (status === void 0) return null;
|
|
215
|
+
if (status === 403) {
|
|
216
|
+
return new AuthenticationError();
|
|
217
|
+
}
|
|
218
|
+
return build(status, void 0, typeof e.message === "string" ? e.message : "");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// src/utils.ts
|
|
222
|
+
var MIN_CFG_SCALE = 1.2;
|
|
223
|
+
var MAX_CFG_SCALE = 2.5;
|
|
224
|
+
function clampCfgScale(cfgScale) {
|
|
225
|
+
if (cfgScale === void 0) return void 0;
|
|
226
|
+
return Math.min(MAX_CFG_SCALE, Math.max(MIN_CFG_SCALE, cfgScale));
|
|
227
|
+
}
|
|
228
|
+
function base64ToArrayBuffer(base64) {
|
|
229
|
+
if (typeof atob === "function") {
|
|
230
|
+
const binary = atob(base64);
|
|
231
|
+
const bytes = new Uint8Array(binary.length);
|
|
232
|
+
for (let i = 0; i < binary.length; i++) {
|
|
233
|
+
bytes[i] = binary.charCodeAt(i);
|
|
234
|
+
}
|
|
235
|
+
return bytes.buffer;
|
|
236
|
+
} else {
|
|
237
|
+
const buffer = Buffer.from(base64, "base64");
|
|
238
|
+
return buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
function decodePCM16(base64) {
|
|
242
|
+
const buffer = base64ToArrayBuffer(base64);
|
|
243
|
+
const int16 = new Int16Array(buffer);
|
|
244
|
+
const float32 = new Float32Array(int16.length);
|
|
245
|
+
for (let i = 0; i < int16.length; i++) {
|
|
246
|
+
float32[i] = int16[i] / 32768;
|
|
247
|
+
}
|
|
248
|
+
return float32;
|
|
249
|
+
}
|
|
250
|
+
function createWavFile(audio, sampleRate) {
|
|
251
|
+
const dataSize = audio.byteLength;
|
|
252
|
+
const fileSize = 44 + dataSize;
|
|
253
|
+
const buffer = new ArrayBuffer(fileSize);
|
|
254
|
+
const view = new DataView(buffer);
|
|
255
|
+
writeString(view, 0, "RIFF");
|
|
256
|
+
view.setUint32(4, fileSize - 8, true);
|
|
257
|
+
writeString(view, 8, "WAVE");
|
|
258
|
+
writeString(view, 12, "fmt ");
|
|
259
|
+
view.setUint32(16, 16, true);
|
|
260
|
+
view.setUint16(20, 1, true);
|
|
261
|
+
view.setUint16(22, 1, true);
|
|
262
|
+
view.setUint32(24, sampleRate, true);
|
|
263
|
+
view.setUint32(28, sampleRate * 2, true);
|
|
264
|
+
view.setUint16(32, 2, true);
|
|
265
|
+
view.setUint16(34, 16, true);
|
|
266
|
+
writeString(view, 36, "data");
|
|
267
|
+
view.setUint32(40, dataSize, true);
|
|
268
|
+
const audioBytes = new Uint8Array(audio);
|
|
269
|
+
const wavBytes = new Uint8Array(buffer);
|
|
270
|
+
wavBytes.set(audioBytes, 44);
|
|
271
|
+
return buffer;
|
|
272
|
+
}
|
|
273
|
+
function writeString(view, offset, str) {
|
|
274
|
+
for (let i = 0; i < str.length; i++) {
|
|
275
|
+
view.setUint8(offset + i, str.charCodeAt(i));
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
function createWavBlob(audio, sampleRate) {
|
|
279
|
+
const wavBuffer = createWavFile(audio, sampleRate);
|
|
280
|
+
return new Blob([wavBuffer], { type: "audio/wav" });
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// package.json
|
|
284
|
+
var package_default = {
|
|
285
|
+
name: "kugelaudio",
|
|
286
|
+
version: "0.9.0",
|
|
287
|
+
description: "Official JavaScript/TypeScript SDK for KugelAudio TTS API",
|
|
288
|
+
main: "dist/index.js",
|
|
289
|
+
module: "dist/index.mjs",
|
|
290
|
+
types: "dist/index.d.ts",
|
|
291
|
+
exports: {
|
|
292
|
+
".": {
|
|
293
|
+
types: "./dist/index.d.ts",
|
|
294
|
+
import: "./dist/index.mjs",
|
|
295
|
+
require: "./dist/index.js"
|
|
296
|
+
},
|
|
297
|
+
"./livekit": {
|
|
298
|
+
types: "./dist/livekit/index.d.ts",
|
|
299
|
+
import: "./dist/livekit/index.mjs",
|
|
300
|
+
require: "./dist/livekit/index.js"
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
files: [
|
|
304
|
+
"dist",
|
|
305
|
+
"src",
|
|
306
|
+
"LICENSE",
|
|
307
|
+
"CHANGELOG.md"
|
|
308
|
+
],
|
|
309
|
+
scripts: {
|
|
310
|
+
build: "tsup src/index.ts src/livekit/index.ts --format cjs,esm --dts",
|
|
311
|
+
dev: "tsup src/index.ts src/livekit/index.ts --format cjs,esm --dts --watch",
|
|
312
|
+
lint: "eslint src/",
|
|
313
|
+
test: "vitest run",
|
|
314
|
+
"test:watch": "vitest",
|
|
315
|
+
prepublishOnly: "npm run build"
|
|
316
|
+
},
|
|
317
|
+
keywords: [
|
|
318
|
+
"tts",
|
|
319
|
+
"text-to-speech",
|
|
320
|
+
"audio",
|
|
321
|
+
"streaming",
|
|
322
|
+
"websocket",
|
|
323
|
+
"kugelaudio"
|
|
324
|
+
],
|
|
325
|
+
author: "KugelAudio <hello@kugelaudio.com>",
|
|
326
|
+
license: "MIT",
|
|
327
|
+
repository: {
|
|
328
|
+
type: "git",
|
|
329
|
+
url: "https://github.com/Kugelaudio/KugelAudio",
|
|
330
|
+
directory: "sdks/js"
|
|
331
|
+
},
|
|
332
|
+
homepage: "https://kugelaudio.com",
|
|
333
|
+
bugs: {
|
|
334
|
+
url: "https://github.com/Kugelaudio/KugelAudio/issues"
|
|
335
|
+
},
|
|
336
|
+
devDependencies: {
|
|
337
|
+
"@livekit/agents": "^1.4.11",
|
|
338
|
+
"@livekit/rtc-node": "^0.13.30",
|
|
339
|
+
"@types/node": "^25.3.2",
|
|
340
|
+
"@types/ws": "^8.18.1",
|
|
341
|
+
tsup: "^8.0.0",
|
|
342
|
+
typescript: "^6.0.2",
|
|
343
|
+
vitest: "^4.0.18"
|
|
344
|
+
},
|
|
345
|
+
peerDependencies: {
|
|
346
|
+
"@livekit/agents": ">=1.0.0",
|
|
347
|
+
"@livekit/rtc-node": ">=0.13.0"
|
|
348
|
+
},
|
|
349
|
+
peerDependenciesMeta: {
|
|
350
|
+
"@livekit/agents": {
|
|
351
|
+
optional: true
|
|
352
|
+
},
|
|
353
|
+
"@livekit/rtc-node": {
|
|
354
|
+
optional: true
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
engines: {
|
|
358
|
+
node: ">=18.0.0"
|
|
359
|
+
},
|
|
360
|
+
dependencies: {
|
|
361
|
+
tsx: "^4.21.0",
|
|
362
|
+
ws: "^8.18.0"
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
export {
|
|
367
|
+
__require,
|
|
368
|
+
__privateGet,
|
|
369
|
+
__privateAdd,
|
|
370
|
+
__privateSet,
|
|
371
|
+
__privateMethod,
|
|
372
|
+
ErrorCodes,
|
|
373
|
+
WsCloseCodes,
|
|
374
|
+
KugelAudioError,
|
|
375
|
+
AuthenticationError,
|
|
376
|
+
RateLimitError,
|
|
377
|
+
InsufficientCreditsError,
|
|
378
|
+
ValidationError,
|
|
379
|
+
ConnectionError,
|
|
380
|
+
NotFoundError,
|
|
381
|
+
classifyHttpError,
|
|
382
|
+
classifyWsFrame,
|
|
383
|
+
classifyWsClose,
|
|
384
|
+
classifyWsHandshakeError,
|
|
385
|
+
clampCfgScale,
|
|
386
|
+
base64ToArrayBuffer,
|
|
387
|
+
decodePCM16,
|
|
388
|
+
createWavFile,
|
|
389
|
+
createWavBlob,
|
|
390
|
+
package_default
|
|
391
|
+
};
|