@lokutor/sdk 1.5.1 → 1.6.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 +29 -2
- package/dist/index.d.mts +34 -21
- package/dist/index.d.ts +34 -21
- package/dist/index.js +151 -86
- package/dist/index.mjs +151 -86
- package/package.json +1 -1
- package/src/client.ts +161 -92
- package/src/types.ts +36 -17
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Official JavaScript/TypeScript SDK for [Lokutor AI](https://lokutor.com), provid
|
|
|
11
11
|
|
|
12
12
|
- ⚡ **Ultra-low latency**: Optimized WebSocket streaming for real-time interactions.
|
|
13
13
|
- 🗣️ **Conversational Intelligence**: Integrated STT, LLM, and TTS with VAD and barge-in support.
|
|
14
|
-
- 🌍 **Multi-lingual**:
|
|
14
|
+
- 🌍 **Multi-lingual**: English, Spanish, Catalan, Galician, Basque, Portuguese, French, Italian and German (Catalan, Galician and Basque are speak-only: no speech recognition yet).
|
|
15
15
|
- 🎨 **Natural Voices**: Multiple high-quality male and female voice styles.
|
|
16
16
|
- 💻 **TypeScript First**: Full type definitions included.
|
|
17
17
|
|
|
@@ -63,9 +63,36 @@ await client.synthesize({
|
|
|
63
63
|
});
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
+
## Using the SDK in a browser without exposing your key
|
|
67
|
+
|
|
68
|
+
Never ship an API key in browser code. Mint a short-lived ticket on your server and let the SDK fetch a
|
|
69
|
+
fresh one before every connect (tickets last 10 minutes, so reconnects need a new one):
|
|
70
|
+
|
|
71
|
+
```js
|
|
72
|
+
// Your server (Node 22+, key stays here)
|
|
73
|
+
app.post('/lokutor-ticket', async (_req, res) => {
|
|
74
|
+
const r = await fetch('https://api.lokutor.com/ws-ticket', {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: { Authorization: `Bearer ${process.env.LOKUTOR_API_KEY}` },
|
|
77
|
+
});
|
|
78
|
+
res.json(await r.json()); // { ticket: "wst_...", expires_in: 600 }
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// The page
|
|
82
|
+
const agent = new VoiceAgentClient({
|
|
83
|
+
auth: async () => (await (await fetch('/lokutor-ticket', { method: 'POST' })).json()).ticket,
|
|
84
|
+
prompt: 'You are a helpful assistant.',
|
|
85
|
+
enableAudio: true,
|
|
86
|
+
});
|
|
87
|
+
await agent.connect();
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
To run an agent you configured in the dashboard, pass `agentId` and leave out `prompt`, `voice` and
|
|
91
|
+
`language`: only what you set explicitly overrides the agent.
|
|
92
|
+
|
|
66
93
|
## Hardware Compatibility
|
|
67
94
|
|
|
68
|
-
This SDK is platform-agnostic and works in both **Node.js** and **Browser** environments.
|
|
95
|
+
This SDK is platform-agnostic and works in both **Node.js** (22 or newer, for the global `WebSocket`) and **Browser** environments.
|
|
69
96
|
|
|
70
97
|
### For Node.js
|
|
71
98
|
We recommend using `node-record-lpcm16` for recording and `speaker` for playback. See the [examples](./examples) folder for a complete Node.js CLI implementation.
|
package/dist/index.d.mts
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* Built-in voices.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Use `F1`–`F5` / `M1`–`M5` (what GET /voices lists). Each is a slot: the server resolves it to the
|
|
8
|
+
* native speaker of the call's language in that slot, so `F1` in Spanish is a Spanish speaker and `F1`
|
|
9
|
+
* in English an English one.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* `EN_*` pin the English speakers regardless of language. `ESCA_*` is the retired Spanish/Catalan pack
|
|
12
|
+
* (its recordings measure as noisy in the top octave); do not use it in new code — `F1`–`M5` with
|
|
13
|
+
* `language: 'es'` gives the vetted Spanish voices.
|
|
14
14
|
*
|
|
15
15
|
* This enum is a convenience, not the full set — a cloned voice's id (`clone_…`) is also a valid
|
|
16
16
|
* `voice`, which is why the config fields accept any string.
|
|
@@ -26,35 +26,35 @@ declare enum VoiceStyle {
|
|
|
26
26
|
EN_M3 = "en_m3",
|
|
27
27
|
EN_M4 = "en_m4",
|
|
28
28
|
EN_M5 = "en_m5",
|
|
29
|
+
/** @deprecated Retired pack; use F1 with language "es". */
|
|
29
30
|
ESCA_F1 = "esca_f1",
|
|
31
|
+
/** @deprecated Retired pack; use F2 with language "es". */
|
|
30
32
|
ESCA_F2 = "esca_f2",
|
|
33
|
+
/** @deprecated Retired pack; use F3 with language "es". */
|
|
31
34
|
ESCA_F3 = "esca_f3",
|
|
35
|
+
/** @deprecated Retired pack; use F4 with language "es". */
|
|
32
36
|
ESCA_F4 = "esca_f4",
|
|
37
|
+
/** @deprecated Retired pack; use F5 with language "es". */
|
|
33
38
|
ESCA_F5 = "esca_f5",
|
|
39
|
+
/** @deprecated Retired pack; use M1 with language "es". */
|
|
34
40
|
ESCA_M1 = "esca_m1",
|
|
41
|
+
/** @deprecated Retired pack; use M2 with language "es". */
|
|
35
42
|
ESCA_M2 = "esca_m2",
|
|
43
|
+
/** @deprecated Retired pack; use M3 with language "es". */
|
|
36
44
|
ESCA_M3 = "esca_m3",
|
|
45
|
+
/** @deprecated Retired pack; use M4 with language "es". */
|
|
37
46
|
ESCA_M4 = "esca_m4",
|
|
47
|
+
/** @deprecated Retired pack; use M5 with language "es". */
|
|
38
48
|
ESCA_M5 = "esca_m5",
|
|
39
|
-
/** @deprecated Versa 1.x name, mapped server-side. Use EN_F1 / ESCA_F1. */
|
|
40
49
|
F1 = "F1",
|
|
41
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
42
50
|
F2 = "F2",
|
|
43
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
44
51
|
F3 = "F3",
|
|
45
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
46
52
|
F4 = "F4",
|
|
47
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
48
53
|
F5 = "F5",
|
|
49
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
50
54
|
M1 = "M1",
|
|
51
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
52
55
|
M2 = "M2",
|
|
53
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
54
56
|
M3 = "M3",
|
|
55
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
56
57
|
M4 = "M4",
|
|
57
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
58
58
|
M5 = "M5"
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
@@ -107,7 +107,14 @@ declare const DEFAULT_URLS: {
|
|
|
107
107
|
* SDK Configuration interface
|
|
108
108
|
*/
|
|
109
109
|
interface LokutorConfig {
|
|
110
|
-
|
|
110
|
+
/** An API key, or a short-lived wst_ ticket from POST /ws-ticket. Optional when `auth` is given. */
|
|
111
|
+
apiKey?: string;
|
|
112
|
+
/**
|
|
113
|
+
* Called before every connect (and reconnect) for the credential to use. In a browser, have it ask
|
|
114
|
+
* your backend for a fresh ticket (POST https://api.lokutor.com/ws-ticket with your API key) so the
|
|
115
|
+
* key itself never reaches the page; a ticket lasts 10 minutes, so reconnects need a new one.
|
|
116
|
+
*/
|
|
117
|
+
auth?: () => string | Promise<string>;
|
|
111
118
|
agentId?: string;
|
|
112
119
|
onTranscription?: (text: string) => void;
|
|
113
120
|
onResponse?: (text: string) => void;
|
|
@@ -285,11 +292,13 @@ interface ToolDefinition {
|
|
|
285
292
|
interface ToolCall {
|
|
286
293
|
name: string;
|
|
287
294
|
arguments: string;
|
|
295
|
+
/** Pass to submitToolResult(call_id, result). */
|
|
296
|
+
call_id: string;
|
|
288
297
|
}
|
|
289
298
|
/**
|
|
290
299
|
* Error code enum matching the backend API error catalog
|
|
291
300
|
*/
|
|
292
|
-
type ErrorCode = 'auth.missing_key' | 'auth.invalid_key' | 'auth.rate_limited' | 'auth.time_limited' | 'validation.invalid_voice' | 'validation.invalid_language' | 'validation.text_too_long' | 'validation.speed_out_of_range' | 'validation.steps_out_of_range' | 'validation.invalid_request_format' | 'tts.synthesis_failed' | 'tts.voice_unavailable' | 'tts.model_not_found' | 'tts.session_limit_reached' | 'stt.not_configured' | 'stt.stream_create_failed' | 'stt.language_not_supported' | 'agent.session_failed' | 'agent.provider_error' | 'session.ended' | 'audio.microphone_unavailable' | 'internal.error' | 'internal.timeout' | 'internal.cancelled' | 'ws.close';
|
|
301
|
+
type ErrorCode = 'auth.missing_key' | 'auth.invalid_key' | 'auth.rate_limited' | 'auth.time_limited' | 'validation.invalid_voice' | 'validation.invalid_language' | 'validation.text_too_long' | 'validation.speed_out_of_range' | 'validation.steps_out_of_range' | 'validation.invalid_request_format' | 'tts.synthesis_failed' | 'tts.voice_unavailable' | 'tts.model_not_found' | 'tts.session_limit_reached' | 'stt.not_configured' | 'stt.stream_create_failed' | 'stt.language_not_supported' | 'agent.session_failed' | 'agent.provider_error' | 'agent.not_found' | 'billing.insufficient_balance' | 'billing.topup_required' | 'billing.limit_reached' | 'billing.account_disputed' | 'session.ended' | 'audio.microphone_unavailable' | 'internal.error' | 'internal.timeout' | 'internal.cancelled' | 'ws.close';
|
|
293
302
|
/**
|
|
294
303
|
* Typed error class for all Lokutor SDK errors.
|
|
295
304
|
* Includes the backend error code, human-readable message,
|
|
@@ -369,8 +378,12 @@ declare class VoiceAgentClient {
|
|
|
369
378
|
private serverPlaybackRate;
|
|
370
379
|
private serverInputRate;
|
|
371
380
|
private serverUrl;
|
|
381
|
+
private authProvider?;
|
|
382
|
+
private voiceExplicit;
|
|
383
|
+
private languageExplicit;
|
|
372
384
|
constructor(config: LokutorConfig & {
|
|
373
|
-
prompt
|
|
385
|
+
/** Required unless agentId is set (a dashboard agent brings its own prompt). */
|
|
386
|
+
prompt?: string;
|
|
374
387
|
voice?: VoiceId;
|
|
375
388
|
language?: Language;
|
|
376
389
|
visemes?: boolean;
|
|
@@ -466,8 +479,8 @@ declare class VoiceAgentClient {
|
|
|
466
479
|
*/
|
|
467
480
|
static fetchStatus(baseUrl?: string): Promise<ServerStatus>;
|
|
468
481
|
/**
|
|
469
|
-
* Fetch
|
|
470
|
-
* No authentication required.
|
|
482
|
+
* Fetch readiness from the server (GET /ready: 200 once it can serve).
|
|
483
|
+
* No authentication required. (/health is the operator dashboard's and needs its password.)
|
|
471
484
|
*/
|
|
472
485
|
static fetchHealth(baseUrl?: string): Promise<HealthStatus>;
|
|
473
486
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
/**
|
|
5
5
|
* Built-in voices.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Use `F1`–`F5` / `M1`–`M5` (what GET /voices lists). Each is a slot: the server resolves it to the
|
|
8
|
+
* native speaker of the call's language in that slot, so `F1` in Spanish is a Spanish speaker and `F1`
|
|
9
|
+
* in English an English one.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* `EN_*` pin the English speakers regardless of language. `ESCA_*` is the retired Spanish/Catalan pack
|
|
12
|
+
* (its recordings measure as noisy in the top octave); do not use it in new code — `F1`–`M5` with
|
|
13
|
+
* `language: 'es'` gives the vetted Spanish voices.
|
|
14
14
|
*
|
|
15
15
|
* This enum is a convenience, not the full set — a cloned voice's id (`clone_…`) is also a valid
|
|
16
16
|
* `voice`, which is why the config fields accept any string.
|
|
@@ -26,35 +26,35 @@ declare enum VoiceStyle {
|
|
|
26
26
|
EN_M3 = "en_m3",
|
|
27
27
|
EN_M4 = "en_m4",
|
|
28
28
|
EN_M5 = "en_m5",
|
|
29
|
+
/** @deprecated Retired pack; use F1 with language "es". */
|
|
29
30
|
ESCA_F1 = "esca_f1",
|
|
31
|
+
/** @deprecated Retired pack; use F2 with language "es". */
|
|
30
32
|
ESCA_F2 = "esca_f2",
|
|
33
|
+
/** @deprecated Retired pack; use F3 with language "es". */
|
|
31
34
|
ESCA_F3 = "esca_f3",
|
|
35
|
+
/** @deprecated Retired pack; use F4 with language "es". */
|
|
32
36
|
ESCA_F4 = "esca_f4",
|
|
37
|
+
/** @deprecated Retired pack; use F5 with language "es". */
|
|
33
38
|
ESCA_F5 = "esca_f5",
|
|
39
|
+
/** @deprecated Retired pack; use M1 with language "es". */
|
|
34
40
|
ESCA_M1 = "esca_m1",
|
|
41
|
+
/** @deprecated Retired pack; use M2 with language "es". */
|
|
35
42
|
ESCA_M2 = "esca_m2",
|
|
43
|
+
/** @deprecated Retired pack; use M3 with language "es". */
|
|
36
44
|
ESCA_M3 = "esca_m3",
|
|
45
|
+
/** @deprecated Retired pack; use M4 with language "es". */
|
|
37
46
|
ESCA_M4 = "esca_m4",
|
|
47
|
+
/** @deprecated Retired pack; use M5 with language "es". */
|
|
38
48
|
ESCA_M5 = "esca_m5",
|
|
39
|
-
/** @deprecated Versa 1.x name, mapped server-side. Use EN_F1 / ESCA_F1. */
|
|
40
49
|
F1 = "F1",
|
|
41
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
42
50
|
F2 = "F2",
|
|
43
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
44
51
|
F3 = "F3",
|
|
45
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
46
52
|
F4 = "F4",
|
|
47
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
48
53
|
F5 = "F5",
|
|
49
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
50
54
|
M1 = "M1",
|
|
51
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
52
55
|
M2 = "M2",
|
|
53
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
54
56
|
M3 = "M3",
|
|
55
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
56
57
|
M4 = "M4",
|
|
57
|
-
/** @deprecated Versa 1.x name, mapped server-side. */
|
|
58
58
|
M5 = "M5"
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
@@ -107,7 +107,14 @@ declare const DEFAULT_URLS: {
|
|
|
107
107
|
* SDK Configuration interface
|
|
108
108
|
*/
|
|
109
109
|
interface LokutorConfig {
|
|
110
|
-
|
|
110
|
+
/** An API key, or a short-lived wst_ ticket from POST /ws-ticket. Optional when `auth` is given. */
|
|
111
|
+
apiKey?: string;
|
|
112
|
+
/**
|
|
113
|
+
* Called before every connect (and reconnect) for the credential to use. In a browser, have it ask
|
|
114
|
+
* your backend for a fresh ticket (POST https://api.lokutor.com/ws-ticket with your API key) so the
|
|
115
|
+
* key itself never reaches the page; a ticket lasts 10 minutes, so reconnects need a new one.
|
|
116
|
+
*/
|
|
117
|
+
auth?: () => string | Promise<string>;
|
|
111
118
|
agentId?: string;
|
|
112
119
|
onTranscription?: (text: string) => void;
|
|
113
120
|
onResponse?: (text: string) => void;
|
|
@@ -285,11 +292,13 @@ interface ToolDefinition {
|
|
|
285
292
|
interface ToolCall {
|
|
286
293
|
name: string;
|
|
287
294
|
arguments: string;
|
|
295
|
+
/** Pass to submitToolResult(call_id, result). */
|
|
296
|
+
call_id: string;
|
|
288
297
|
}
|
|
289
298
|
/**
|
|
290
299
|
* Error code enum matching the backend API error catalog
|
|
291
300
|
*/
|
|
292
|
-
type ErrorCode = 'auth.missing_key' | 'auth.invalid_key' | 'auth.rate_limited' | 'auth.time_limited' | 'validation.invalid_voice' | 'validation.invalid_language' | 'validation.text_too_long' | 'validation.speed_out_of_range' | 'validation.steps_out_of_range' | 'validation.invalid_request_format' | 'tts.synthesis_failed' | 'tts.voice_unavailable' | 'tts.model_not_found' | 'tts.session_limit_reached' | 'stt.not_configured' | 'stt.stream_create_failed' | 'stt.language_not_supported' | 'agent.session_failed' | 'agent.provider_error' | 'session.ended' | 'audio.microphone_unavailable' | 'internal.error' | 'internal.timeout' | 'internal.cancelled' | 'ws.close';
|
|
301
|
+
type ErrorCode = 'auth.missing_key' | 'auth.invalid_key' | 'auth.rate_limited' | 'auth.time_limited' | 'validation.invalid_voice' | 'validation.invalid_language' | 'validation.text_too_long' | 'validation.speed_out_of_range' | 'validation.steps_out_of_range' | 'validation.invalid_request_format' | 'tts.synthesis_failed' | 'tts.voice_unavailable' | 'tts.model_not_found' | 'tts.session_limit_reached' | 'stt.not_configured' | 'stt.stream_create_failed' | 'stt.language_not_supported' | 'agent.session_failed' | 'agent.provider_error' | 'agent.not_found' | 'billing.insufficient_balance' | 'billing.topup_required' | 'billing.limit_reached' | 'billing.account_disputed' | 'session.ended' | 'audio.microphone_unavailable' | 'internal.error' | 'internal.timeout' | 'internal.cancelled' | 'ws.close';
|
|
293
302
|
/**
|
|
294
303
|
* Typed error class for all Lokutor SDK errors.
|
|
295
304
|
* Includes the backend error code, human-readable message,
|
|
@@ -369,8 +378,12 @@ declare class VoiceAgentClient {
|
|
|
369
378
|
private serverPlaybackRate;
|
|
370
379
|
private serverInputRate;
|
|
371
380
|
private serverUrl;
|
|
381
|
+
private authProvider?;
|
|
382
|
+
private voiceExplicit;
|
|
383
|
+
private languageExplicit;
|
|
372
384
|
constructor(config: LokutorConfig & {
|
|
373
|
-
prompt
|
|
385
|
+
/** Required unless agentId is set (a dashboard agent brings its own prompt). */
|
|
386
|
+
prompt?: string;
|
|
374
387
|
voice?: VoiceId;
|
|
375
388
|
language?: Language;
|
|
376
389
|
visemes?: boolean;
|
|
@@ -466,8 +479,8 @@ declare class VoiceAgentClient {
|
|
|
466
479
|
*/
|
|
467
480
|
static fetchStatus(baseUrl?: string): Promise<ServerStatus>;
|
|
468
481
|
/**
|
|
469
|
-
* Fetch
|
|
470
|
-
* No authentication required.
|
|
482
|
+
* Fetch readiness from the server (GET /ready: 200 once it can serve).
|
|
483
|
+
* No authentication required. (/health is the operator dashboard's and needs its password.)
|
|
471
484
|
*/
|
|
472
485
|
static fetchHealth(baseUrl?: string): Promise<HealthStatus>;
|
|
473
486
|
/**
|