@voice-ai-labs/web-sdk 0.3.0 → 0.5.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/README.md +184 -40
- package/dist/client/agents.d.ts +4 -5
- package/dist/client/agents.d.ts.map +1 -1
- package/dist/client/agents.js +7 -6
- package/dist/client/agents.js.map +1 -1
- package/dist/client/base.d.ts +1 -1
- package/dist/client/base.d.ts.map +1 -1
- package/dist/client/base.js +1 -1
- package/dist/client/base.js.map +1 -1
- package/dist/client/index.d.ts +3 -62
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +3 -64
- package/dist/client/index.js.map +1 -1
- package/dist/client/knowledge-base.js +1 -1
- package/dist/client/knowledge-base.js.map +1 -1
- package/dist/client/tts.d.ts +227 -0
- package/dist/client/tts.d.ts.map +1 -0
- package/dist/client/tts.js +259 -0
- package/dist/client/tts.js.map +1 -0
- package/dist/index.d.ts +85 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +754 -49
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +754 -49
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +86 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text-to-Speech (TTS) Client
|
|
3
|
+
*
|
|
4
|
+
* Provides methods for:
|
|
5
|
+
* - Generating speech from text (synchronous and streaming)
|
|
6
|
+
* - Listing available voices
|
|
7
|
+
* - Getting voice details
|
|
8
|
+
* - Cloning voices from audio samples
|
|
9
|
+
* - Updating and deleting voices
|
|
10
|
+
*/
|
|
11
|
+
import { BaseClient, BaseClientConfig } from './base';
|
|
12
|
+
import type { SynthesizeRequest, CloneVoiceOptions, CloneVoiceResponse, VoiceResponse, UpdateVoiceOptions, DeleteVoiceResponse } from '../types';
|
|
13
|
+
/**
|
|
14
|
+
* Client for Text-to-Speech operations
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* // List available voices
|
|
19
|
+
* const voices = await client.tts.listVoices();
|
|
20
|
+
*
|
|
21
|
+
* // Generate speech
|
|
22
|
+
* const audio = await client.tts.synthesize({
|
|
23
|
+
* text: 'Hello world!',
|
|
24
|
+
* voice_id: 'voice-123',
|
|
25
|
+
* language: 'en',
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* // Clone a voice
|
|
29
|
+
* const cloned = await client.tts.cloneVoice({
|
|
30
|
+
* file: audioFile,
|
|
31
|
+
* name: 'My Voice',
|
|
32
|
+
* language: 'en',
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare class TTSClient extends BaseClient {
|
|
37
|
+
constructor(config: BaseClientConfig);
|
|
38
|
+
/**
|
|
39
|
+
* Generate speech from text (returns complete audio file)
|
|
40
|
+
*
|
|
41
|
+
* This is the synchronous endpoint - it blocks until generation completes
|
|
42
|
+
* and returns the entire audio file as a Blob. For lower latency with
|
|
43
|
+
* chunked streaming, use {@link synthesizeStream}.
|
|
44
|
+
*
|
|
45
|
+
* @param options - Speech generation options
|
|
46
|
+
* @returns Audio blob in the requested format
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const audio = await client.tts.synthesize({
|
|
51
|
+
* text: 'Hello, welcome to Voice AI!',
|
|
52
|
+
* voice_id: 'voice-123',
|
|
53
|
+
* language: 'en',
|
|
54
|
+
* audio_format: 'mp3',
|
|
55
|
+
* });
|
|
56
|
+
*
|
|
57
|
+
* // Play in browser
|
|
58
|
+
* const url = URL.createObjectURL(audio);
|
|
59
|
+
* new Audio(url).play();
|
|
60
|
+
*
|
|
61
|
+
* // Or download
|
|
62
|
+
* const a = document.createElement('a');
|
|
63
|
+
* a.href = url;
|
|
64
|
+
* a.download = 'speech.mp3';
|
|
65
|
+
* a.click();
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
synthesize(options: SynthesizeRequest): Promise<Blob>;
|
|
69
|
+
/**
|
|
70
|
+
* Generate speech from text with HTTP chunked streaming
|
|
71
|
+
*
|
|
72
|
+
* Returns a Response object with a readable body stream. Audio chunks
|
|
73
|
+
* are sent to the client as they are generated, providing lower perceived
|
|
74
|
+
* latency than {@link synthesize}.
|
|
75
|
+
*
|
|
76
|
+
* @param options - Speech generation options
|
|
77
|
+
* @returns Fetch Response with streaming body
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```typescript
|
|
81
|
+
* const response = await client.tts.synthesizeStream({
|
|
82
|
+
* text: 'Hello, welcome to Voice AI!',
|
|
83
|
+
* voice_id: 'voice-123',
|
|
84
|
+
* language: 'en',
|
|
85
|
+
* });
|
|
86
|
+
*
|
|
87
|
+
* // Read chunks as they arrive
|
|
88
|
+
* const reader = response.body!.getReader();
|
|
89
|
+
* while (true) {
|
|
90
|
+
* const { done, value } = await reader.read();
|
|
91
|
+
* if (done) break;
|
|
92
|
+
* // Process audio chunk (Uint8Array)
|
|
93
|
+
* console.log('Received chunk:', value.length, 'bytes');
|
|
94
|
+
* }
|
|
95
|
+
*
|
|
96
|
+
* // Or collect all chunks and create a blob
|
|
97
|
+
* const response = await client.tts.synthesizeStream({ text: '...', voice_id: '...' });
|
|
98
|
+
* const blob = await response.blob();
|
|
99
|
+
* ```
|
|
100
|
+
*/
|
|
101
|
+
synthesizeStream(options: SynthesizeRequest): Promise<Response>;
|
|
102
|
+
/**
|
|
103
|
+
* List voices available to the current user
|
|
104
|
+
*
|
|
105
|
+
* Returns voices owned by the authenticated user plus default/public voices.
|
|
106
|
+
* Deleted voices are excluded.
|
|
107
|
+
*
|
|
108
|
+
* @returns Array of voice objects
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```typescript
|
|
112
|
+
* const voices = await client.tts.listVoices();
|
|
113
|
+
*
|
|
114
|
+
* for (const voice of voices) {
|
|
115
|
+
* console.log(`${voice.name} (${voice.voice_id}): ${voice.status}`);
|
|
116
|
+
* }
|
|
117
|
+
*
|
|
118
|
+
* // Filter to only available voices
|
|
119
|
+
* const available = voices.filter(v => v.status === 'AVAILABLE');
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
listVoices(): Promise<VoiceResponse[]>;
|
|
123
|
+
/**
|
|
124
|
+
* Get voice details and status
|
|
125
|
+
*
|
|
126
|
+
* Useful for polling the status of a voice clone operation
|
|
127
|
+
* (PENDING -> PROCESSING -> AVAILABLE).
|
|
128
|
+
*
|
|
129
|
+
* Access control:
|
|
130
|
+
* - PUBLIC voices: readable by any authenticated user
|
|
131
|
+
* - PRIVATE voices: readable only by owner (returns 404 otherwise)
|
|
132
|
+
*
|
|
133
|
+
* @param voiceId - The voice ID
|
|
134
|
+
* @returns Voice details and status
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```typescript
|
|
138
|
+
* const voice = await client.tts.getVoice('voice-123');
|
|
139
|
+
*
|
|
140
|
+
* if (voice.status === 'AVAILABLE') {
|
|
141
|
+
* console.log('Voice is ready to use!');
|
|
142
|
+
* } else if (voice.status === 'PROCESSING') {
|
|
143
|
+
* console.log('Voice is still being processed...');
|
|
144
|
+
* }
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
getVoice(voiceId: string): Promise<VoiceResponse>;
|
|
148
|
+
/**
|
|
149
|
+
* Clone a voice from a reference audio file
|
|
150
|
+
*
|
|
151
|
+
* Accepts an audio file (MP3, WAV, or OGG, max 7.5MB) and creates
|
|
152
|
+
* a cloned voice. The voice starts in PENDING status and moves to
|
|
153
|
+
* PROCESSING, then AVAILABLE once ready.
|
|
154
|
+
*
|
|
155
|
+
* Use {@link getVoice} to poll the voice status.
|
|
156
|
+
*
|
|
157
|
+
* @param options - Clone voice options including audio file
|
|
158
|
+
* @returns Created voice with ID and initial status (PENDING)
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```typescript
|
|
162
|
+
* // From a file input
|
|
163
|
+
* const fileInput = document.querySelector('input[type="file"]');
|
|
164
|
+
* const file = fileInput.files[0];
|
|
165
|
+
*
|
|
166
|
+
* const voice = await client.tts.cloneVoice({
|
|
167
|
+
* file: file,
|
|
168
|
+
* name: 'My Custom Voice',
|
|
169
|
+
* language: 'en',
|
|
170
|
+
* voice_visibility: 'PRIVATE',
|
|
171
|
+
* });
|
|
172
|
+
*
|
|
173
|
+
* console.log('Voice ID:', voice.voice_id);
|
|
174
|
+
* console.log('Status:', voice.status); // 'PENDING'
|
|
175
|
+
*
|
|
176
|
+
* // Poll until ready
|
|
177
|
+
* let status = voice.status;
|
|
178
|
+
* while (status !== 'AVAILABLE' && status !== 'FAILED') {
|
|
179
|
+
* await new Promise(r => setTimeout(r, 2000));
|
|
180
|
+
* const updated = await client.tts.getVoice(voice.voice_id);
|
|
181
|
+
* status = updated.status;
|
|
182
|
+
* console.log('Status:', status);
|
|
183
|
+
* }
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
cloneVoice(options: CloneVoiceOptions): Promise<CloneVoiceResponse>;
|
|
187
|
+
/**
|
|
188
|
+
* Update voice metadata (name and/or visibility)
|
|
189
|
+
*
|
|
190
|
+
* Only the voice owner can update a voice.
|
|
191
|
+
*
|
|
192
|
+
* @param voiceId - The voice ID to update
|
|
193
|
+
* @param options - Fields to update
|
|
194
|
+
* @returns Updated voice details
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```typescript
|
|
198
|
+
* // Rename a voice
|
|
199
|
+
* const updated = await client.tts.updateVoice('voice-123', {
|
|
200
|
+
* name: 'New Voice Name',
|
|
201
|
+
* });
|
|
202
|
+
*
|
|
203
|
+
* // Make a voice private
|
|
204
|
+
* const updated = await client.tts.updateVoice('voice-123', {
|
|
205
|
+
* voice_visibility: 'PRIVATE',
|
|
206
|
+
* });
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
updateVoice(voiceId: string, options: UpdateVoiceOptions): Promise<VoiceResponse>;
|
|
210
|
+
/**
|
|
211
|
+
* Delete a voice
|
|
212
|
+
*
|
|
213
|
+
* Only the voice owner can delete a voice. The voice will no longer
|
|
214
|
+
* appear in voice listings.
|
|
215
|
+
*
|
|
216
|
+
* @param voiceId - The voice ID to delete
|
|
217
|
+
* @returns Deletion confirmation
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```typescript
|
|
221
|
+
* await client.tts.deleteVoice('voice-123');
|
|
222
|
+
* console.log('Voice deleted');
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
deleteVoice(voiceId: string): Promise<DeleteVoiceResponse>;
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=tts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../../src/client/tts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,SAAU,SAAQ,UAAU;gBAC3B,MAAM,EAAE,gBAAgB;IAQpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACG,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACG,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAQrE;;;;;;;;;;;;;;;;;;;OAmBG;IACG,UAAU,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAI5C;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAIvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IACG,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAiBzE;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC;IAIvF;;;;;;;;;;;;;;OAcG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;CAGjE"}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text-to-Speech (TTS) Client
|
|
3
|
+
*
|
|
4
|
+
* Provides methods for:
|
|
5
|
+
* - Generating speech from text (synchronous and streaming)
|
|
6
|
+
* - Listing available voices
|
|
7
|
+
* - Getting voice details
|
|
8
|
+
* - Cloning voices from audio samples
|
|
9
|
+
* - Updating and deleting voices
|
|
10
|
+
*/
|
|
11
|
+
import { BaseClient } from './base';
|
|
12
|
+
/**
|
|
13
|
+
* Client for Text-to-Speech operations
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```typescript
|
|
17
|
+
* // List available voices
|
|
18
|
+
* const voices = await client.tts.listVoices();
|
|
19
|
+
*
|
|
20
|
+
* // Generate speech
|
|
21
|
+
* const audio = await client.tts.synthesize({
|
|
22
|
+
* text: 'Hello world!',
|
|
23
|
+
* voice_id: 'voice-123',
|
|
24
|
+
* language: 'en',
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* // Clone a voice
|
|
28
|
+
* const cloned = await client.tts.cloneVoice({
|
|
29
|
+
* file: audioFile,
|
|
30
|
+
* name: 'My Voice',
|
|
31
|
+
* language: 'en',
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export class TTSClient extends BaseClient {
|
|
36
|
+
constructor(config) {
|
|
37
|
+
super(config);
|
|
38
|
+
}
|
|
39
|
+
// ==========================================================================
|
|
40
|
+
// SPEECH GENERATION
|
|
41
|
+
// ==========================================================================
|
|
42
|
+
/**
|
|
43
|
+
* Generate speech from text (returns complete audio file)
|
|
44
|
+
*
|
|
45
|
+
* This is the synchronous endpoint - it blocks until generation completes
|
|
46
|
+
* and returns the entire audio file as a Blob. For lower latency with
|
|
47
|
+
* chunked streaming, use {@link synthesizeStream}.
|
|
48
|
+
*
|
|
49
|
+
* @param options - Speech generation options
|
|
50
|
+
* @returns Audio blob in the requested format
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* const audio = await client.tts.synthesize({
|
|
55
|
+
* text: 'Hello, welcome to Voice AI!',
|
|
56
|
+
* voice_id: 'voice-123',
|
|
57
|
+
* language: 'en',
|
|
58
|
+
* audio_format: 'mp3',
|
|
59
|
+
* });
|
|
60
|
+
*
|
|
61
|
+
* // Play in browser
|
|
62
|
+
* const url = URL.createObjectURL(audio);
|
|
63
|
+
* new Audio(url).play();
|
|
64
|
+
*
|
|
65
|
+
* // Or download
|
|
66
|
+
* const a = document.createElement('a');
|
|
67
|
+
* a.href = url;
|
|
68
|
+
* a.download = 'speech.mp3';
|
|
69
|
+
* a.click();
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
async synthesize(options) {
|
|
73
|
+
return this.postForBlob('/tts/speech', options);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Generate speech from text with HTTP chunked streaming
|
|
77
|
+
*
|
|
78
|
+
* Returns a Response object with a readable body stream. Audio chunks
|
|
79
|
+
* are sent to the client as they are generated, providing lower perceived
|
|
80
|
+
* latency than {@link synthesize}.
|
|
81
|
+
*
|
|
82
|
+
* @param options - Speech generation options
|
|
83
|
+
* @returns Fetch Response with streaming body
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const response = await client.tts.synthesizeStream({
|
|
88
|
+
* text: 'Hello, welcome to Voice AI!',
|
|
89
|
+
* voice_id: 'voice-123',
|
|
90
|
+
* language: 'en',
|
|
91
|
+
* });
|
|
92
|
+
*
|
|
93
|
+
* // Read chunks as they arrive
|
|
94
|
+
* const reader = response.body!.getReader();
|
|
95
|
+
* while (true) {
|
|
96
|
+
* const { done, value } = await reader.read();
|
|
97
|
+
* if (done) break;
|
|
98
|
+
* // Process audio chunk (Uint8Array)
|
|
99
|
+
* console.log('Received chunk:', value.length, 'bytes');
|
|
100
|
+
* }
|
|
101
|
+
*
|
|
102
|
+
* // Or collect all chunks and create a blob
|
|
103
|
+
* const response = await client.tts.synthesizeStream({ text: '...', voice_id: '...' });
|
|
104
|
+
* const blob = await response.blob();
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
async synthesizeStream(options) {
|
|
108
|
+
return this.postForStream('/tts/speech/stream', options);
|
|
109
|
+
}
|
|
110
|
+
// ==========================================================================
|
|
111
|
+
// VOICE MANAGEMENT
|
|
112
|
+
// ==========================================================================
|
|
113
|
+
/**
|
|
114
|
+
* List voices available to the current user
|
|
115
|
+
*
|
|
116
|
+
* Returns voices owned by the authenticated user plus default/public voices.
|
|
117
|
+
* Deleted voices are excluded.
|
|
118
|
+
*
|
|
119
|
+
* @returns Array of voice objects
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* const voices = await client.tts.listVoices();
|
|
124
|
+
*
|
|
125
|
+
* for (const voice of voices) {
|
|
126
|
+
* console.log(`${voice.name} (${voice.voice_id}): ${voice.status}`);
|
|
127
|
+
* }
|
|
128
|
+
*
|
|
129
|
+
* // Filter to only available voices
|
|
130
|
+
* const available = voices.filter(v => v.status === 'AVAILABLE');
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
async listVoices() {
|
|
134
|
+
return this.get('/tts/voices');
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get voice details and status
|
|
138
|
+
*
|
|
139
|
+
* Useful for polling the status of a voice clone operation
|
|
140
|
+
* (PENDING -> PROCESSING -> AVAILABLE).
|
|
141
|
+
*
|
|
142
|
+
* Access control:
|
|
143
|
+
* - PUBLIC voices: readable by any authenticated user
|
|
144
|
+
* - PRIVATE voices: readable only by owner (returns 404 otherwise)
|
|
145
|
+
*
|
|
146
|
+
* @param voiceId - The voice ID
|
|
147
|
+
* @returns Voice details and status
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```typescript
|
|
151
|
+
* const voice = await client.tts.getVoice('voice-123');
|
|
152
|
+
*
|
|
153
|
+
* if (voice.status === 'AVAILABLE') {
|
|
154
|
+
* console.log('Voice is ready to use!');
|
|
155
|
+
* } else if (voice.status === 'PROCESSING') {
|
|
156
|
+
* console.log('Voice is still being processed...');
|
|
157
|
+
* }
|
|
158
|
+
* ```
|
|
159
|
+
*/
|
|
160
|
+
async getVoice(voiceId) {
|
|
161
|
+
return this.get(`/tts/voice/${encodeURIComponent(voiceId)}`);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Clone a voice from a reference audio file
|
|
165
|
+
*
|
|
166
|
+
* Accepts an audio file (MP3, WAV, or OGG, max 7.5MB) and creates
|
|
167
|
+
* a cloned voice. The voice starts in PENDING status and moves to
|
|
168
|
+
* PROCESSING, then AVAILABLE once ready.
|
|
169
|
+
*
|
|
170
|
+
* Use {@link getVoice} to poll the voice status.
|
|
171
|
+
*
|
|
172
|
+
* @param options - Clone voice options including audio file
|
|
173
|
+
* @returns Created voice with ID and initial status (PENDING)
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```typescript
|
|
177
|
+
* // From a file input
|
|
178
|
+
* const fileInput = document.querySelector('input[type="file"]');
|
|
179
|
+
* const file = fileInput.files[0];
|
|
180
|
+
*
|
|
181
|
+
* const voice = await client.tts.cloneVoice({
|
|
182
|
+
* file: file,
|
|
183
|
+
* name: 'My Custom Voice',
|
|
184
|
+
* language: 'en',
|
|
185
|
+
* voice_visibility: 'PRIVATE',
|
|
186
|
+
* });
|
|
187
|
+
*
|
|
188
|
+
* console.log('Voice ID:', voice.voice_id);
|
|
189
|
+
* console.log('Status:', voice.status); // 'PENDING'
|
|
190
|
+
*
|
|
191
|
+
* // Poll until ready
|
|
192
|
+
* let status = voice.status;
|
|
193
|
+
* while (status !== 'AVAILABLE' && status !== 'FAILED') {
|
|
194
|
+
* await new Promise(r => setTimeout(r, 2000));
|
|
195
|
+
* const updated = await client.tts.getVoice(voice.voice_id);
|
|
196
|
+
* status = updated.status;
|
|
197
|
+
* console.log('Status:', status);
|
|
198
|
+
* }
|
|
199
|
+
* ```
|
|
200
|
+
*/
|
|
201
|
+
async cloneVoice(options) {
|
|
202
|
+
const formData = new FormData();
|
|
203
|
+
formData.append('file', options.file);
|
|
204
|
+
if (options.name !== undefined) {
|
|
205
|
+
formData.append('name', options.name);
|
|
206
|
+
}
|
|
207
|
+
if (options.voice_visibility !== undefined) {
|
|
208
|
+
formData.append('voice_visibility', options.voice_visibility);
|
|
209
|
+
}
|
|
210
|
+
if (options.language !== undefined) {
|
|
211
|
+
formData.append('language', options.language);
|
|
212
|
+
}
|
|
213
|
+
return this.postFormData('/tts/clone-voice', formData);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Update voice metadata (name and/or visibility)
|
|
217
|
+
*
|
|
218
|
+
* Only the voice owner can update a voice.
|
|
219
|
+
*
|
|
220
|
+
* @param voiceId - The voice ID to update
|
|
221
|
+
* @param options - Fields to update
|
|
222
|
+
* @returns Updated voice details
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```typescript
|
|
226
|
+
* // Rename a voice
|
|
227
|
+
* const updated = await client.tts.updateVoice('voice-123', {
|
|
228
|
+
* name: 'New Voice Name',
|
|
229
|
+
* });
|
|
230
|
+
*
|
|
231
|
+
* // Make a voice private
|
|
232
|
+
* const updated = await client.tts.updateVoice('voice-123', {
|
|
233
|
+
* voice_visibility: 'PRIVATE',
|
|
234
|
+
* });
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
async updateVoice(voiceId, options) {
|
|
238
|
+
return this.patch(`/tts/voice/${encodeURIComponent(voiceId)}`, options);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Delete a voice
|
|
242
|
+
*
|
|
243
|
+
* Only the voice owner can delete a voice. The voice will no longer
|
|
244
|
+
* appear in voice listings.
|
|
245
|
+
*
|
|
246
|
+
* @param voiceId - The voice ID to delete
|
|
247
|
+
* @returns Deletion confirmation
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```typescript
|
|
251
|
+
* await client.tts.deleteVoice('voice-123');
|
|
252
|
+
* console.log('Voice deleted');
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
255
|
+
async deleteVoice(voiceId) {
|
|
256
|
+
return this.httpDelete(`/tts/voice/${encodeURIComponent(voiceId)}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=tts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tts.js","sourceRoot":"","sources":["../../src/client/tts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAoB,MAAM,QAAQ,CAAC;AAUtD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,OAAO,SAAU,SAAQ,UAAU;IACvC,YAAY,MAAwB;QAClC,KAAK,CAAC,MAAM,CAAC,CAAC;IAChB,CAAC;IAED,6EAA6E;IAC7E,oBAAoB;IACpB,6EAA6E;IAE7E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,KAAK,CAAC,UAAU,CAAC,OAA0B;QACzC,OAAO,IAAI,CAAC,WAAW,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,KAAK,CAAC,gBAAgB,CAAC,OAA0B;QAC/C,OAAO,IAAI,CAAC,aAAa,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC;IAC3D,CAAC;IAED,6EAA6E;IAC7E,mBAAmB;IACnB,6EAA6E;IAE7E;;;;;;;;;;;;;;;;;;;OAmBG;IACH,KAAK,CAAC,UAAU;QACd,OAAO,IAAI,CAAC,GAAG,CAAkB,aAAa,CAAC,CAAC;IAClD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,KAAK,CAAC,QAAQ,CAAC,OAAe;QAC5B,OAAO,IAAI,CAAC,GAAG,CAAgB,cAAc,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAqCG;IACH,KAAK,CAAC,UAAU,CAAC,OAA0B;QACzC,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;QAChC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAEtC,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC/B,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC;QACD,IAAI,OAAO,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;YAC3C,QAAQ,CAAC,MAAM,CAAC,kBAAkB,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;QAChE,CAAC;QACD,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACnC,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;QAChD,CAAC;QAED,OAAO,IAAI,CAAC,YAAY,CAAqB,kBAAkB,EAAE,QAAQ,CAAC,CAAC;IAC7E,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,KAAK,CAAC,WAAW,CAAC,OAAe,EAAE,OAA2B;QAC5D,OAAO,IAAI,CAAC,KAAK,CAAgB,cAAc,kBAAkB,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACzF,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,WAAW,CAAC,OAAe;QAC/B,OAAO,IAAI,CAAC,UAAU,CAAsB,cAAc,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC3F,CAAC;CACF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -24,7 +24,8 @@ import { AgentClient } from './client/agents';
|
|
|
24
24
|
import { AnalyticsClient } from './client/analytics';
|
|
25
25
|
import { KnowledgeBaseClient } from './client/knowledge-base';
|
|
26
26
|
import { PhoneNumberClient } from './client/phone-numbers';
|
|
27
|
-
import
|
|
27
|
+
import { TTSClient } from './client/tts';
|
|
28
|
+
import type { ConnectionOptions, ConnectionDetails, ConnectionStatus, TranscriptionHandler, ConnectionStatusHandler, ErrorHandler, AudioCaptureOptions, AgentStateInfo, MicrophoneState, AgentStateHandler, AudioLevelHandler, MicrophoneStateHandler, VoiceAIConfig } from './types';
|
|
28
29
|
/**
|
|
29
30
|
* VoiceAI - The unified Voice.ai SDK
|
|
30
31
|
*
|
|
@@ -50,14 +51,21 @@ import type { ConnectionOptions, ConnectionStatus, TranscriptionHandler, Connect
|
|
|
50
51
|
* ```
|
|
51
52
|
*/
|
|
52
53
|
export declare class VoiceAI {
|
|
53
|
-
/** Agent management - create, update, deploy, pause, delete agents */
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
|
|
54
|
+
/** Agent management - create, update, deploy, pause, delete agents. */
|
|
55
|
+
get agents(): AgentClient;
|
|
56
|
+
private _agents?;
|
|
57
|
+
/** Analytics - call history, transcripts, stats. */
|
|
58
|
+
get analytics(): AnalyticsClient;
|
|
59
|
+
private _analytics?;
|
|
60
|
+
/** Knowledge Base - manage RAG documents. */
|
|
61
|
+
get knowledgeBase(): KnowledgeBaseClient;
|
|
62
|
+
private _knowledgeBase?;
|
|
63
|
+
/** Phone Numbers - search, select, release phone numbers. */
|
|
64
|
+
get phoneNumbers(): PhoneNumberClient;
|
|
65
|
+
private _phoneNumbers?;
|
|
66
|
+
/** Text-to-Speech - generate speech, manage voices, clone voices. */
|
|
67
|
+
get tts(): TTSClient;
|
|
68
|
+
private _tts?;
|
|
61
69
|
private room;
|
|
62
70
|
private connectionStatus;
|
|
63
71
|
private transcriptionHandlers;
|
|
@@ -68,6 +76,7 @@ export declare class VoiceAI {
|
|
|
68
76
|
private microphoneStateHandlers;
|
|
69
77
|
private apiUrl;
|
|
70
78
|
private apiKey;
|
|
79
|
+
private effectiveApiKey;
|
|
71
80
|
private cachedConnectionDetails;
|
|
72
81
|
private currentAgentState;
|
|
73
82
|
private agentParticipantId;
|
|
@@ -75,17 +84,61 @@ export declare class VoiceAI {
|
|
|
75
84
|
/**
|
|
76
85
|
* Create a new VoiceAI client
|
|
77
86
|
*
|
|
78
|
-
* @param config - Configuration options
|
|
79
|
-
* @param config.apiKey - Your Voice.ai API key (required)
|
|
87
|
+
* @param config - Configuration options (optional for frontend-only usage)
|
|
88
|
+
* @param config.apiKey - Your Voice.ai API key (required for API operations and `getConnectionDetails`)
|
|
80
89
|
* @param config.apiUrl - Custom API URL (optional, defaults to production)
|
|
81
90
|
*/
|
|
82
|
-
constructor(config
|
|
91
|
+
constructor(config?: VoiceAIConfig);
|
|
83
92
|
/**
|
|
84
|
-
*
|
|
93
|
+
* Get connection details for a voice agent.
|
|
94
|
+
*
|
|
95
|
+
* Requires an API key. Call this from your backend, then pass the result
|
|
96
|
+
* to `connectRoom()` on the frontend.
|
|
97
|
+
*
|
|
98
|
+
* @param options - Connection options (agentId, testMode, etc.)
|
|
99
|
+
* @returns Connection details (serverUrl, participantToken, callId)
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* // Server-side: get connection details
|
|
104
|
+
* const details = await voiceai.getConnectionDetails({ agentId: 'agent-123' });
|
|
105
|
+
* // Return details to frontend...
|
|
106
|
+
*
|
|
107
|
+
* // With test mode
|
|
108
|
+
* const details = await voiceai.getConnectionDetails({
|
|
109
|
+
* agentId: 'agent-123',
|
|
110
|
+
* testMode: true
|
|
111
|
+
* });
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
getConnectionDetails(options: ConnectionOptions): Promise<ConnectionDetails>;
|
|
115
|
+
/**
|
|
116
|
+
* Connect to a LiveKit room using pre-fetched connection details.
|
|
117
|
+
*
|
|
118
|
+
* This is the browser-safe method -- it only needs a room token,
|
|
119
|
+
* no API key required. Get the connection details from your backend
|
|
120
|
+
* using `getConnectionDetails()`.
|
|
121
|
+
*
|
|
122
|
+
* @param connectionDetails - Server URL, participant token, and call ID from your backend
|
|
123
|
+
* @param options - Audio/microphone options
|
|
124
|
+
*
|
|
125
|
+
* @example
|
|
126
|
+
* ```typescript
|
|
127
|
+
* // Frontend: connect using token from your backend
|
|
128
|
+
* const voiceai = new VoiceAI();
|
|
129
|
+
* await voiceai.connectRoom(
|
|
130
|
+
* { serverUrl, participantToken, callId },
|
|
131
|
+
* { autoPublishMic: true }
|
|
132
|
+
* );
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
135
|
+
connectRoom(connectionDetails: ConnectionDetails, options?: Pick<ConnectionOptions, 'autoPublishMic' | 'audioOptions' | 'preConnectBuffer'>): Promise<void>;
|
|
136
|
+
/**
|
|
137
|
+
* Connect to a voice agent for real-time conversation.
|
|
138
|
+
*
|
|
139
|
+
* Convenience method that combines `getConnectionDetails()` + `connectRoom()`.
|
|
85
140
|
*
|
|
86
141
|
* @param options - Connection options
|
|
87
|
-
* @param options.agentId - ID of the agent to connect to
|
|
88
|
-
* @param options.autoPublishMic - Auto-enable microphone (default: true)
|
|
89
142
|
*
|
|
90
143
|
* @example
|
|
91
144
|
* ```typescript
|
|
@@ -94,7 +147,12 @@ export declare class VoiceAI {
|
|
|
94
147
|
*/
|
|
95
148
|
connect(options: ConnectionOptions): Promise<void>;
|
|
96
149
|
/**
|
|
97
|
-
* Disconnect from the
|
|
150
|
+
* Disconnect from the room and end the call.
|
|
151
|
+
*
|
|
152
|
+
* Signals the server to free the concurrency slot using endToken from
|
|
153
|
+
* connection details. Connection details always include end_token when
|
|
154
|
+
* fetched from the API; backends must pass it when using pre-fetched details.
|
|
155
|
+
* If no endToken, the server detects room disconnect as fallback.
|
|
98
156
|
*/
|
|
99
157
|
disconnect(): Promise<void>;
|
|
100
158
|
/**
|
|
@@ -153,7 +211,16 @@ export declare class VoiceAI {
|
|
|
153
211
|
onMicrophoneStateChange(handler: MicrophoneStateHandler): () => void;
|
|
154
212
|
private isTokenExpired;
|
|
155
213
|
private getOrRefreshConnectionDetails;
|
|
156
|
-
|
|
214
|
+
/**
|
|
215
|
+
* Fetch connection details from the developer's backend endpoint.
|
|
216
|
+
* The backend holds the API key and calls the Voice.AI API server-side.
|
|
217
|
+
*/
|
|
218
|
+
/**
|
|
219
|
+
* Fetch connection details using the API key directly.
|
|
220
|
+
* Used by the public getConnectionDetails() method.
|
|
221
|
+
*/
|
|
222
|
+
private fetchConnectionDetails;
|
|
223
|
+
private fetchConnectionDetailsFromApi;
|
|
157
224
|
private setupAudio;
|
|
158
225
|
private setupRoomListeners;
|
|
159
226
|
private detectAgentParticipant;
|
|
@@ -171,7 +238,7 @@ export declare class VoiceAI {
|
|
|
171
238
|
export default VoiceAI;
|
|
172
239
|
/** Error class for API errors */
|
|
173
240
|
export { VoiceAIError } from './client/base';
|
|
174
|
-
export type { VoiceAIConfig, ConnectionOptions, ConnectionDetails, ConnectionStatus, TranscriptionSegment,
|
|
241
|
+
export type { VoiceAIConfig, ConnectionOptions, ConnectionDetails, ConnectionStatus, TranscriptionSegment, AgentState, AgentStateInfo, AudioLevelInfo, MicrophoneState, Agent, VoiceResponse, VoiceStatus, } from './types';
|
|
175
242
|
/**
|
|
176
243
|
* Generate optimized audio capture options for voice agents
|
|
177
244
|
*
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|