kugelaudio 0.8.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.
@@ -0,0 +1,222 @@
1
+ import { tts, TimedString, APIConnectOptions } from '@livekit/agents';
2
+
3
+ /**
4
+ * Models, constants, and language validation for the KugelAudio LiveKit
5
+ * Agents TTS plugin.
6
+ *
7
+ * This module has NO dependency on `@livekit/agents`, so it can be imported
8
+ * and unit-tested without the (heavy, native) LiveKit runtime.
9
+ */
10
+ /**
11
+ * Available TTS models. Legacy ids (`kugel-2.5`, `kugel-2-turbo`,
12
+ * `kugel-1-turbo`, `kugel-1`) remain accepted for back-compat — server-side
13
+ * they all alias to `kugel-3` — but new code should use `'kugel-3'`.
14
+ */
15
+ type TTSModels = 'kugel-3' | 'kugel-2.5' | 'kugel-2-turbo' | 'kugel-1-turbo' | 'kugel-1';
16
+ /** Default model (matches the public API default; server-side `is_default=kugel-3`). */
17
+ declare const DEFAULT_MODEL: TTSModels;
18
+ /**
19
+ * Supported output sample rates. The model generates at 24 kHz natively;
20
+ * other rates use server-side resampling.
21
+ */
22
+ declare const SUPPORTED_SAMPLE_RATES: readonly [24000, 22050, 16000, 8000];
23
+ /** Default output sample rate. */
24
+ declare const DEFAULT_SAMPLE_RATE = 24000;
25
+ /** Default voice id (`null` means use the server default voice). */
26
+ declare const DEFAULT_VOICE_ID: number | null;
27
+ /** Default classifier-free guidance scale. */
28
+ declare const DEFAULT_CFG_SCALE = 2;
29
+ /** Default maximum number of tokens to generate. */
30
+ declare const DEFAULT_MAX_NEW_TOKENS = 2048;
31
+ /**
32
+ * ISO 639-1 language codes accepted by the API for text normalization.
33
+ * Mirrors `kugelaudio.livekit.tts.SUPPORTED_LANGUAGES` in the Python SDK.
34
+ */
35
+ declare const SUPPORTED_LANGUAGES: ReadonlySet<string>;
36
+ /**
37
+ * Validate that `language` is an ISO 639-1 code supported by the API.
38
+ *
39
+ * @param language - The language code, or `undefined`/`null` to leave unset.
40
+ * @returns The validated code, or `undefined` when none was supplied.
41
+ * @throws {Error} If `language` is not a supported ISO 639-1 code. A common
42
+ * mistake is passing a BCP 47 locale tag such as `'de-DE'` instead of `'de'`.
43
+ */
44
+ declare function validateLanguage(language: string | null | undefined): string | undefined;
45
+
46
+ /**
47
+ * Pure wire-protocol helpers for the KugelAudio `/ws/tts/multi` endpoint used
48
+ * by the LiveKit plugin.
49
+ *
50
+ * Kept free of any `@livekit/agents` import so the message-shaping logic can be
51
+ * unit-tested without the LiveKit runtime. Mirrors the send loop of
52
+ * `kugelaudio.livekit.tts._Connection` in the Python SDK.
53
+ */
54
+ /** Generation parameters carried on the first message of each context. */
55
+ interface WireOptions {
56
+ model: string;
57
+ voiceId: number | null;
58
+ sampleRate: number;
59
+ cfgScale: number;
60
+ maxNewTokens: number;
61
+ wordTimestamps: boolean;
62
+ normalize: boolean;
63
+ language?: string;
64
+ }
65
+
66
+ /**
67
+ * KugelAudio TTS plugin for the LiveKit Agents (Node.js) framework.
68
+ *
69
+ * A TypeScript translation of `kugelaudio.livekit.tts` (Python SDK). Uses a
70
+ * single persistent WebSocket to `/ws/tts/multi` with per-call context IDs; the
71
+ * shared connection multiplexes every context. On barge-in (the framework
72
+ * aborting a stream) the context is closed with `immediate=true` and any late
73
+ * server frames for it are silently discarded. A context's waiter resolves only
74
+ * on the server's `context_closed` frame — sent after every audio frame has
75
+ * been drained — so the audio tail is never clipped.
76
+ */
77
+
78
+ /** Options accepted by the {@link TTS} constructor. */
79
+ interface TTSOptions {
80
+ /**
81
+ * KugelAudio API key. Falls back to the `KUGELAUDIO_API_KEY` environment
82
+ * variable. Prefix with `"eu-"` to select the direct EU endpoint; the prefix
83
+ * is stripped before auth.
84
+ */
85
+ apiKey?: string;
86
+ /** TTS model. Defaults to `'kugel-3'`. */
87
+ model?: TTSModels | string;
88
+ /** Voice id. `null`/omitted uses the server default voice. */
89
+ voiceId?: number | null;
90
+ /** Output sample rate in Hz (24000, 22050, 16000, 8000). Defaults to 24000. */
91
+ sampleRate?: number;
92
+ /** Classifier-free guidance scale. Clamped to [1.2, 2.5]. Defaults to 2.0. */
93
+ cfgScale?: number;
94
+ /** Maximum tokens to generate. Defaults to 2048. */
95
+ maxNewTokens?: number;
96
+ /** Apply loudness normalization to the output audio. Defaults to true. */
97
+ normalize?: boolean;
98
+ /**
99
+ * Request per-chunk word-level timestamps for aligned transcript / barge-in.
100
+ * Defaults to false. Enabling advertises the `alignedTranscript` capability.
101
+ */
102
+ wordTimestamps?: boolean;
103
+ /**
104
+ * ISO 639-1 language code for text normalization (e.g. `'de'`). When set,
105
+ * skips server-side auto-detection, saving ~60-150ms per request.
106
+ */
107
+ language?: string;
108
+ /** API base URL. Overrides the default geo-routed endpoint. */
109
+ baseURL?: string;
110
+ }
111
+ interface ResolvedTTSOptions extends WireOptions {
112
+ apiKey: string;
113
+ baseURL: string;
114
+ }
115
+ /** A resolvable/rejectable promise handle. */
116
+ interface Deferred {
117
+ promise: Promise<void>;
118
+ resolve: () => void;
119
+ reject: (err: Error) => void;
120
+ settled: boolean;
121
+ }
122
+ /**
123
+ * Turns raw PCM byte chunks into LiveKit {@link AudioFrame}s, deferring the
124
+ * last produced frame so it can be flagged `final` once the segment ends. Word
125
+ * timings are attached to the next emitted frame. Mirrors the frame-emission
126
+ * behaviour of the Python plugin's `AudioEmitter`.
127
+ */
128
+ declare class AudioSink {
129
+ #private;
130
+ private readonly put;
131
+ private readonly requestId;
132
+ private readonly segmentId;
133
+ constructor(sampleRate: number, put: (audio: tts.SynthesizedAudio) => void, requestId: string, segmentId: string);
134
+ pushAudio(bytes: Buffer): void;
135
+ pushTimed(words: TimedString[]): void;
136
+ /** Flush any buffered audio and emit the terminal (`final`) frame. */
137
+ end(): void;
138
+ }
139
+ interface ContextData {
140
+ sink: AudioSink;
141
+ waiter: Deferred;
142
+ isStreaming: boolean;
143
+ lastActivityAt: number;
144
+ }
145
+ /**
146
+ * Single persistent WebSocket to `/ws/tts/multi` with background send/recv
147
+ * loops. Each synthesis registers a unique `context_id`; the recv loop routes
148
+ * server messages by id and silently drops messages for unknown/closed
149
+ * contexts, which is what makes barge-in safe.
150
+ */
151
+ declare class Connection {
152
+ #private;
153
+ constructor(opts: ResolvedTTSOptions);
154
+ get isCurrent(): boolean;
155
+ get closed(): boolean;
156
+ markNonCurrent(): void;
157
+ connect(timeoutMs?: number): Promise<void>;
158
+ registerContext(contextId: string, sink: AudioSink, waiter: Deferred, isStreaming: boolean): ContextData | null;
159
+ sendText(contextId: string, text: string, flush: boolean): void;
160
+ closeContext(contextId: string, immediate: boolean): void;
161
+ cleanupContext(contextId: string): void;
162
+ close(): Promise<void>;
163
+ }
164
+ /**
165
+ * KugelAudio Text-to-Speech plugin for LiveKit Agents.
166
+ *
167
+ * @example
168
+ * ```ts
169
+ * import { TTS } from 'kugelaudio/livekit';
170
+ * import { AgentSession } from '@livekit/agents';
171
+ *
172
+ * const session = new AgentSession({
173
+ * tts: new TTS({ voiceId: 1071, model: 'kugel-3', language: 'en' }),
174
+ * // ...stt, llm, vad
175
+ * });
176
+ * ```
177
+ */
178
+ declare class TTS extends tts.TTS {
179
+ #private;
180
+ label: string;
181
+ constructor(opts?: TTSOptions);
182
+ get model(): string;
183
+ get provider(): string;
184
+ /** Get or create the persistent `/ws/tts/multi` connection. */
185
+ currentConnection(timeoutMs?: number): Promise<Connection>;
186
+ /**
187
+ * Eagerly establish the WebSocket connection to remove handshake latency
188
+ * from the first synthesis. Errors are logged and retried on first use.
189
+ */
190
+ prewarm(): void;
191
+ /**
192
+ * Update TTS options at runtime. Changing any option marks the current
193
+ * connection non-current so the next synthesis opens a fresh one.
194
+ */
195
+ updateOptions(opts: Partial<Omit<TTSOptions, 'apiKey' | 'baseURL'>>): void;
196
+ synthesize(text: string, connOptions?: APIConnectOptions, abortSignal?: AbortSignal): ChunkedStream;
197
+ stream(options?: {
198
+ connOptions?: APIConnectOptions;
199
+ }): SynthesizeStream;
200
+ close(): Promise<void>;
201
+ }
202
+ /** Non-streaming (one-shot) synthesis over the shared `/ws/tts/multi` connection. */
203
+ declare class ChunkedStream extends tts.ChunkedStream {
204
+ #private;
205
+ label: string;
206
+ constructor(ttsInstance: TTS, text: string, opts: ResolvedTTSOptions, connOptions?: APIConnectOptions, abortSignal?: AbortSignal);
207
+ protected run(): Promise<void>;
208
+ }
209
+ /**
210
+ * Streaming synthesis: text tokens are pushed via the LiveKit input channel and
211
+ * forwarded to the shared connection; audio is routed back by `context_id`.
212
+ */
213
+ declare class SynthesizeStream extends tts.SynthesizeStream {
214
+ #private;
215
+ label: string;
216
+ constructor(ttsInstance: TTS, opts: ResolvedTTSOptions, connOptions?: APIConnectOptions);
217
+ /** The server-side context id of the current (latest) run attempt. */
218
+ get contextId(): string | undefined;
219
+ protected run(): Promise<void>;
220
+ }
221
+
222
+ export { ChunkedStream, DEFAULT_CFG_SCALE, DEFAULT_MAX_NEW_TOKENS, DEFAULT_MODEL, DEFAULT_SAMPLE_RATE, DEFAULT_VOICE_ID, SUPPORTED_LANGUAGES, SUPPORTED_SAMPLE_RATES, SynthesizeStream, TTS, type TTSModels, type TTSOptions, validateLanguage };
@@ -0,0 +1,222 @@
1
+ import { tts, TimedString, APIConnectOptions } from '@livekit/agents';
2
+
3
+ /**
4
+ * Models, constants, and language validation for the KugelAudio LiveKit
5
+ * Agents TTS plugin.
6
+ *
7
+ * This module has NO dependency on `@livekit/agents`, so it can be imported
8
+ * and unit-tested without the (heavy, native) LiveKit runtime.
9
+ */
10
+ /**
11
+ * Available TTS models. Legacy ids (`kugel-2.5`, `kugel-2-turbo`,
12
+ * `kugel-1-turbo`, `kugel-1`) remain accepted for back-compat — server-side
13
+ * they all alias to `kugel-3` — but new code should use `'kugel-3'`.
14
+ */
15
+ type TTSModels = 'kugel-3' | 'kugel-2.5' | 'kugel-2-turbo' | 'kugel-1-turbo' | 'kugel-1';
16
+ /** Default model (matches the public API default; server-side `is_default=kugel-3`). */
17
+ declare const DEFAULT_MODEL: TTSModels;
18
+ /**
19
+ * Supported output sample rates. The model generates at 24 kHz natively;
20
+ * other rates use server-side resampling.
21
+ */
22
+ declare const SUPPORTED_SAMPLE_RATES: readonly [24000, 22050, 16000, 8000];
23
+ /** Default output sample rate. */
24
+ declare const DEFAULT_SAMPLE_RATE = 24000;
25
+ /** Default voice id (`null` means use the server default voice). */
26
+ declare const DEFAULT_VOICE_ID: number | null;
27
+ /** Default classifier-free guidance scale. */
28
+ declare const DEFAULT_CFG_SCALE = 2;
29
+ /** Default maximum number of tokens to generate. */
30
+ declare const DEFAULT_MAX_NEW_TOKENS = 2048;
31
+ /**
32
+ * ISO 639-1 language codes accepted by the API for text normalization.
33
+ * Mirrors `kugelaudio.livekit.tts.SUPPORTED_LANGUAGES` in the Python SDK.
34
+ */
35
+ declare const SUPPORTED_LANGUAGES: ReadonlySet<string>;
36
+ /**
37
+ * Validate that `language` is an ISO 639-1 code supported by the API.
38
+ *
39
+ * @param language - The language code, or `undefined`/`null` to leave unset.
40
+ * @returns The validated code, or `undefined` when none was supplied.
41
+ * @throws {Error} If `language` is not a supported ISO 639-1 code. A common
42
+ * mistake is passing a BCP 47 locale tag such as `'de-DE'` instead of `'de'`.
43
+ */
44
+ declare function validateLanguage(language: string | null | undefined): string | undefined;
45
+
46
+ /**
47
+ * Pure wire-protocol helpers for the KugelAudio `/ws/tts/multi` endpoint used
48
+ * by the LiveKit plugin.
49
+ *
50
+ * Kept free of any `@livekit/agents` import so the message-shaping logic can be
51
+ * unit-tested without the LiveKit runtime. Mirrors the send loop of
52
+ * `kugelaudio.livekit.tts._Connection` in the Python SDK.
53
+ */
54
+ /** Generation parameters carried on the first message of each context. */
55
+ interface WireOptions {
56
+ model: string;
57
+ voiceId: number | null;
58
+ sampleRate: number;
59
+ cfgScale: number;
60
+ maxNewTokens: number;
61
+ wordTimestamps: boolean;
62
+ normalize: boolean;
63
+ language?: string;
64
+ }
65
+
66
+ /**
67
+ * KugelAudio TTS plugin for the LiveKit Agents (Node.js) framework.
68
+ *
69
+ * A TypeScript translation of `kugelaudio.livekit.tts` (Python SDK). Uses a
70
+ * single persistent WebSocket to `/ws/tts/multi` with per-call context IDs; the
71
+ * shared connection multiplexes every context. On barge-in (the framework
72
+ * aborting a stream) the context is closed with `immediate=true` and any late
73
+ * server frames for it are silently discarded. A context's waiter resolves only
74
+ * on the server's `context_closed` frame — sent after every audio frame has
75
+ * been drained — so the audio tail is never clipped.
76
+ */
77
+
78
+ /** Options accepted by the {@link TTS} constructor. */
79
+ interface TTSOptions {
80
+ /**
81
+ * KugelAudio API key. Falls back to the `KUGELAUDIO_API_KEY` environment
82
+ * variable. Prefix with `"eu-"` to select the direct EU endpoint; the prefix
83
+ * is stripped before auth.
84
+ */
85
+ apiKey?: string;
86
+ /** TTS model. Defaults to `'kugel-3'`. */
87
+ model?: TTSModels | string;
88
+ /** Voice id. `null`/omitted uses the server default voice. */
89
+ voiceId?: number | null;
90
+ /** Output sample rate in Hz (24000, 22050, 16000, 8000). Defaults to 24000. */
91
+ sampleRate?: number;
92
+ /** Classifier-free guidance scale. Clamped to [1.2, 2.5]. Defaults to 2.0. */
93
+ cfgScale?: number;
94
+ /** Maximum tokens to generate. Defaults to 2048. */
95
+ maxNewTokens?: number;
96
+ /** Apply loudness normalization to the output audio. Defaults to true. */
97
+ normalize?: boolean;
98
+ /**
99
+ * Request per-chunk word-level timestamps for aligned transcript / barge-in.
100
+ * Defaults to false. Enabling advertises the `alignedTranscript` capability.
101
+ */
102
+ wordTimestamps?: boolean;
103
+ /**
104
+ * ISO 639-1 language code for text normalization (e.g. `'de'`). When set,
105
+ * skips server-side auto-detection, saving ~60-150ms per request.
106
+ */
107
+ language?: string;
108
+ /** API base URL. Overrides the default geo-routed endpoint. */
109
+ baseURL?: string;
110
+ }
111
+ interface ResolvedTTSOptions extends WireOptions {
112
+ apiKey: string;
113
+ baseURL: string;
114
+ }
115
+ /** A resolvable/rejectable promise handle. */
116
+ interface Deferred {
117
+ promise: Promise<void>;
118
+ resolve: () => void;
119
+ reject: (err: Error) => void;
120
+ settled: boolean;
121
+ }
122
+ /**
123
+ * Turns raw PCM byte chunks into LiveKit {@link AudioFrame}s, deferring the
124
+ * last produced frame so it can be flagged `final` once the segment ends. Word
125
+ * timings are attached to the next emitted frame. Mirrors the frame-emission
126
+ * behaviour of the Python plugin's `AudioEmitter`.
127
+ */
128
+ declare class AudioSink {
129
+ #private;
130
+ private readonly put;
131
+ private readonly requestId;
132
+ private readonly segmentId;
133
+ constructor(sampleRate: number, put: (audio: tts.SynthesizedAudio) => void, requestId: string, segmentId: string);
134
+ pushAudio(bytes: Buffer): void;
135
+ pushTimed(words: TimedString[]): void;
136
+ /** Flush any buffered audio and emit the terminal (`final`) frame. */
137
+ end(): void;
138
+ }
139
+ interface ContextData {
140
+ sink: AudioSink;
141
+ waiter: Deferred;
142
+ isStreaming: boolean;
143
+ lastActivityAt: number;
144
+ }
145
+ /**
146
+ * Single persistent WebSocket to `/ws/tts/multi` with background send/recv
147
+ * loops. Each synthesis registers a unique `context_id`; the recv loop routes
148
+ * server messages by id and silently drops messages for unknown/closed
149
+ * contexts, which is what makes barge-in safe.
150
+ */
151
+ declare class Connection {
152
+ #private;
153
+ constructor(opts: ResolvedTTSOptions);
154
+ get isCurrent(): boolean;
155
+ get closed(): boolean;
156
+ markNonCurrent(): void;
157
+ connect(timeoutMs?: number): Promise<void>;
158
+ registerContext(contextId: string, sink: AudioSink, waiter: Deferred, isStreaming: boolean): ContextData | null;
159
+ sendText(contextId: string, text: string, flush: boolean): void;
160
+ closeContext(contextId: string, immediate: boolean): void;
161
+ cleanupContext(contextId: string): void;
162
+ close(): Promise<void>;
163
+ }
164
+ /**
165
+ * KugelAudio Text-to-Speech plugin for LiveKit Agents.
166
+ *
167
+ * @example
168
+ * ```ts
169
+ * import { TTS } from 'kugelaudio/livekit';
170
+ * import { AgentSession } from '@livekit/agents';
171
+ *
172
+ * const session = new AgentSession({
173
+ * tts: new TTS({ voiceId: 1071, model: 'kugel-3', language: 'en' }),
174
+ * // ...stt, llm, vad
175
+ * });
176
+ * ```
177
+ */
178
+ declare class TTS extends tts.TTS {
179
+ #private;
180
+ label: string;
181
+ constructor(opts?: TTSOptions);
182
+ get model(): string;
183
+ get provider(): string;
184
+ /** Get or create the persistent `/ws/tts/multi` connection. */
185
+ currentConnection(timeoutMs?: number): Promise<Connection>;
186
+ /**
187
+ * Eagerly establish the WebSocket connection to remove handshake latency
188
+ * from the first synthesis. Errors are logged and retried on first use.
189
+ */
190
+ prewarm(): void;
191
+ /**
192
+ * Update TTS options at runtime. Changing any option marks the current
193
+ * connection non-current so the next synthesis opens a fresh one.
194
+ */
195
+ updateOptions(opts: Partial<Omit<TTSOptions, 'apiKey' | 'baseURL'>>): void;
196
+ synthesize(text: string, connOptions?: APIConnectOptions, abortSignal?: AbortSignal): ChunkedStream;
197
+ stream(options?: {
198
+ connOptions?: APIConnectOptions;
199
+ }): SynthesizeStream;
200
+ close(): Promise<void>;
201
+ }
202
+ /** Non-streaming (one-shot) synthesis over the shared `/ws/tts/multi` connection. */
203
+ declare class ChunkedStream extends tts.ChunkedStream {
204
+ #private;
205
+ label: string;
206
+ constructor(ttsInstance: TTS, text: string, opts: ResolvedTTSOptions, connOptions?: APIConnectOptions, abortSignal?: AbortSignal);
207
+ protected run(): Promise<void>;
208
+ }
209
+ /**
210
+ * Streaming synthesis: text tokens are pushed via the LiveKit input channel and
211
+ * forwarded to the shared connection; audio is routed back by `context_id`.
212
+ */
213
+ declare class SynthesizeStream extends tts.SynthesizeStream {
214
+ #private;
215
+ label: string;
216
+ constructor(ttsInstance: TTS, opts: ResolvedTTSOptions, connOptions?: APIConnectOptions);
217
+ /** The server-side context id of the current (latest) run attempt. */
218
+ get contextId(): string | undefined;
219
+ protected run(): Promise<void>;
220
+ }
221
+
222
+ export { ChunkedStream, DEFAULT_CFG_SCALE, DEFAULT_MAX_NEW_TOKENS, DEFAULT_MODEL, DEFAULT_SAMPLE_RATE, DEFAULT_VOICE_ID, SUPPORTED_LANGUAGES, SUPPORTED_SAMPLE_RATES, SynthesizeStream, TTS, type TTSModels, type TTSOptions, validateLanguage };