@rimelabs/sdk 0.1.0-alpha.2 → 0.1.0-alpha.21
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 +155 -0
- package/LICENSE-PRISM +201 -0
- package/NOTICE-PRISM +9 -0
- package/README.md +279 -114
- package/dist/auth.d.ts +9 -3
- package/dist/auth.js +12 -4
- package/dist/cancellation.d.ts +6 -0
- package/dist/cancellation.js +54 -0
- package/dist/client.d.ts +24 -23
- package/dist/client.js +41 -92
- package/dist/grpc.d.ts +27 -0
- package/dist/grpc.js +175 -0
- package/dist/index.d.ts +10 -2
- package/dist/index.js +6 -2
- package/dist/pcm.d.ts +21 -0
- package/dist/pcm.js +52 -0
- package/dist/realtime/async.d.ts +23 -0
- package/dist/realtime/async.js +87 -0
- package/dist/realtime/audio.d.ts +2 -0
- package/dist/realtime/audio.js +11 -0
- package/dist/realtime/client.d.ts +12 -0
- package/dist/realtime/client.js +139 -0
- package/dist/realtime/conversation.d.ts +47 -0
- package/dist/realtime/conversation.js +321 -0
- package/dist/realtime/faults.d.ts +2 -0
- package/dist/realtime/faults.js +25 -0
- package/dist/realtime/protocol.d.ts +99 -0
- package/dist/realtime/protocol.js +432 -0
- package/dist/realtime/session.d.ts +79 -0
- package/dist/realtime/session.js +808 -0
- package/dist/realtime/settings.d.ts +11 -0
- package/dist/realtime/settings.js +142 -0
- package/dist/realtime/transport.d.ts +16 -0
- package/dist/realtime/transport.js +86 -0
- package/dist/realtime/types.d.ts +292 -0
- package/dist/realtime/types.js +12 -0
- package/dist/stt/audio.d.ts +15 -0
- package/dist/stt/audio.js +46 -0
- package/dist/stt/client.d.ts +17 -0
- package/dist/stt/client.js +121 -0
- package/dist/stt/policy.d.ts +11 -0
- package/dist/stt/policy.js +11 -0
- package/dist/stt/protocol.d.ts +14 -0
- package/dist/stt/protocol.js +76 -0
- package/dist/stt/queue.d.ts +16 -0
- package/dist/stt/queue.js +45 -0
- package/dist/stt/stream.d.ts +48 -0
- package/dist/stt/stream.js +224 -0
- package/dist/stt/transport.d.ts +15 -0
- package/dist/stt/transport.js +94 -0
- package/dist/stt/types.d.ts +51 -0
- package/dist/stt/types.js +1 -0
- package/dist/{audio.d.ts → tts/audio.d.ts} +9 -0
- package/dist/{audio.js → tts/audio.js} +44 -4
- package/dist/tts/buffer.d.ts +3 -0
- package/dist/tts/buffer.js +53 -0
- package/dist/tts/client.d.ts +53 -0
- package/dist/tts/client.js +151 -0
- package/dist/tts/lexicon.d.ts +7 -0
- package/dist/tts/lexicon.js +23 -0
- package/dist/{policy.d.ts → tts/policy.d.ts} +2 -3
- package/dist/tts/policy.js +41 -0
- package/dist/{stream.d.ts → tts/stream.d.ts} +15 -2
- package/dist/{stream.js → tts/stream.js} +54 -30
- package/dist/tts/timestamps.d.ts +24 -0
- package/dist/tts/timestamps.js +46 -0
- package/dist/{transport.d.ts → tts/transport.d.ts} +10 -13
- package/dist/tts/transport.js +133 -0
- package/dist/validation.d.ts +5 -0
- package/dist/validation.js +28 -0
- package/docs/realtime.md +537 -0
- package/docs/stt.md +186 -0
- package/docs/tts.md +284 -0
- package/package.json +14 -12
- package/dist/policy.js +0 -82
- package/dist/sentences.d.ts +0 -21
- package/dist/sentences.js +0 -142
- package/dist/transport.js +0 -223
- package/vendor/LICENSE.blingfire +0 -21
- package/vendor/README.md +0 -11
- package/vendor/blingfire.cjs +0 -4
- package/vendor/blingfire.upstream.cjs +0 -1
- package/vendor/blingfire.wasm +0 -0
- /package/dist/{queue.d.ts → tts/queue.d.ts} +0 -0
- /package/dist/{queue.js → tts/queue.js} +0 -0
package/README.md
CHANGED
|
@@ -1,166 +1,331 @@
|
|
|
1
|
-
# Rime SDK for Node.js
|
|
1
|
+
# Rime SDK for TypeScript and Node.js
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Generate speech with Coda or Mist v3, transcribe audio with STT, and build spoken
|
|
4
|
+
conversations with Prism through the `Rime` client. Your application handles
|
|
5
|
+
audio capture and playback.
|
|
4
6
|
|
|
5
|
-
##
|
|
7
|
+
## Installation
|
|
6
8
|
|
|
7
|
-
Requires Node.js 22 or later
|
|
8
|
-
types
|
|
9
|
+
Requires **Node.js 22 or later**. Install the alpha package from npm. It uses
|
|
10
|
+
ESM, includes TypeScript types, and runs on a server; browsers are not supported.
|
|
9
11
|
|
|
10
12
|
```sh
|
|
11
|
-
|
|
13
|
+
mkdir rime-quickstart
|
|
14
|
+
cd rime-quickstart
|
|
15
|
+
npm init -y
|
|
16
|
+
npm install @rimelabs/sdk wavefile
|
|
12
17
|
export RIME_API_KEY="your-api-key"
|
|
13
18
|
```
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
For an existing project, skip the first three commands. Get an API key from the
|
|
21
|
+
[Rime dashboard](https://app.rime.ai/). A key is required: `new Rime()` reads
|
|
22
|
+
`RIME_API_KEY`, or you can pass `new Rime({ apiKey: "..." })` to override it.
|
|
23
|
+
The SDK does not load `.env` files. Shell commands below assume macOS or Linux.
|
|
16
24
|
|
|
17
|
-
|
|
25
|
+
Examples use JavaScript `.mjs` files so you can run them without a TypeScript build.
|
|
26
|
+
The additional `wavefile` dependency is only needed by the Prism example; TTS and
|
|
27
|
+
STT use the SDK and Node.js built-ins.
|
|
28
|
+
|
|
29
|
+
## Prism: send recorded speech
|
|
30
|
+
|
|
31
|
+
With `RIME_API_KEY` set, the SDK connects to
|
|
32
|
+
`wss://prism.api.rime.ai/v1/realtime` and uses the server's default voice.
|
|
33
|
+
Pass `endpoint` or `voice` to select another deployment or voice.
|
|
34
|
+
|
|
35
|
+
Record a short question and export it as `question.wav`, using **16 kHz, mono, signed 16-bit PCM WAV**.
|
|
36
|
+
Save the following as `prism.mjs` beside that file. It sends the recording at capture speed,
|
|
37
|
+
then sends silence so Prism can detect the end of speech. The spoken reply goes to `reply.wav`.
|
|
18
38
|
|
|
19
39
|
```javascript
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
40
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
41
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
42
|
+
import wavefile from "wavefile";
|
|
43
|
+
import { Rime, RimeRealtimeError } from "@rimelabs/sdk";
|
|
44
|
+
|
|
45
|
+
const input = new wavefile.WaveFile(readFileSync("question.wav"));
|
|
46
|
+
if (
|
|
47
|
+
input.container !== "RIFF" ||
|
|
48
|
+
input.fmt.audioFormat !== 1 ||
|
|
49
|
+
input.fmt.numChannels !== 1 ||
|
|
50
|
+
input.fmt.sampleRate !== 16000 ||
|
|
51
|
+
input.bitDepth !== "16"
|
|
52
|
+
) {
|
|
53
|
+
throw new Error("question.wav must be 16 kHz mono PCM16");
|
|
54
|
+
}
|
|
55
|
+
const data = Buffer.from(input.data.samples);
|
|
56
|
+
if (!data.length || data.length % 2)
|
|
57
|
+
throw new Error("question.wav has no complete PCM16 audio");
|
|
22
58
|
|
|
23
59
|
const client = new Rime();
|
|
60
|
+
const stop = new AbortController();
|
|
61
|
+
const deadline = setTimeout(() => {
|
|
62
|
+
stop.abort();
|
|
63
|
+
void client.close();
|
|
64
|
+
}, 60_000);
|
|
65
|
+
let sending;
|
|
66
|
+
let reading;
|
|
24
67
|
try {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
68
|
+
const session = await client.realtime.connect({
|
|
69
|
+
instructions: "Give short, clear answers.",
|
|
70
|
+
});
|
|
71
|
+
sending = (async () => {
|
|
72
|
+
for (let offset = 0; ; offset += 1280) {
|
|
73
|
+
const frame = Buffer.alloc(1280);
|
|
74
|
+
if (offset < data.length)
|
|
75
|
+
data.copy(frame, 0, offset, Math.min(offset + 1280, data.length));
|
|
76
|
+
await session.sendAudio({ data: frame });
|
|
77
|
+
await sleep(40, undefined, { signal: stop.signal });
|
|
78
|
+
}
|
|
79
|
+
})();
|
|
80
|
+
const chunks = [];
|
|
81
|
+
reading = (async () => {
|
|
82
|
+
for await (const { payload } of session.events) {
|
|
83
|
+
if (payload.kind === "audio.delta")
|
|
84
|
+
chunks.push(Buffer.from(payload.audio.data));
|
|
85
|
+
if (payload.kind === "error") throw new RimeRealtimeError(payload.error);
|
|
86
|
+
if (payload.kind === "response.ended") {
|
|
87
|
+
if (payload.status !== "completed") {
|
|
88
|
+
throw new Error(`Response ${payload.status}: ${payload.reason}`);
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
throw new Error("Session closed before the reply ended");
|
|
94
|
+
})();
|
|
95
|
+
await Promise.race([sending, reading]);
|
|
96
|
+
const pcm = Buffer.concat(chunks);
|
|
97
|
+
const samples = Array.from({ length: pcm.length / 2 }, (_, i) =>
|
|
98
|
+
pcm.readInt16LE(i * 2),
|
|
99
|
+
);
|
|
100
|
+
const wav = new wavefile.WaveFile();
|
|
101
|
+
wav.fromScratch(1, 24000, "16", samples);
|
|
102
|
+
writeFileSync("reply.wav", wav.toBuffer());
|
|
103
|
+
console.log("Saved reply.wav");
|
|
28
104
|
} finally {
|
|
105
|
+
stop.abort();
|
|
106
|
+
clearTimeout(deadline);
|
|
29
107
|
await client.close();
|
|
108
|
+
await Promise.allSettled([sending, reading]);
|
|
30
109
|
}
|
|
31
110
|
```
|
|
32
111
|
|
|
33
|
-
|
|
34
|
-
|
|
112
|
+
Run `node prism.mjs`, then open `reply.wav`. This example has a 60-second deadline
|
|
113
|
+
and collects the reply in memory. It saves audio without playing it, so it sends no playback receipt.
|
|
114
|
+
For live conversations, your application must capture microphone audio, play replies,
|
|
115
|
+
stop playback on interruption, and report the actual played position to Prism.
|
|
116
|
+
Keep the API key on your server when you add a browser interface.
|
|
117
|
+
TTS settings do not configure Prism sessions.
|
|
35
118
|
|
|
36
|
-
##
|
|
37
|
-
|
|
38
|
-
Add this async text source before the client declaration in the quick start:
|
|
39
|
-
|
|
40
|
-
```javascript
|
|
41
|
-
async function* text() {
|
|
42
|
-
yield "Hello. ";
|
|
43
|
-
yield "This text arrives in separate chunks.";
|
|
44
|
-
}
|
|
45
|
-
```
|
|
119
|
+
## TTS
|
|
46
120
|
|
|
47
|
-
Use `client.tts.
|
|
48
|
-
|
|
49
|
-
|
|
121
|
+
Use `client.tts.synthesize(text, options)` for a complete string, or
|
|
122
|
+
`client.tts.stream(textChunks, options)` for an async iterable of strings.
|
|
123
|
+
Both return an `AudioStream` immediately; consume it with `for await`, without
|
|
124
|
+
awaiting the method call. For complete audio in memory, await
|
|
125
|
+
`client.tts.synthesizeToBuffer(text, options)` to receive a Node.js `Buffer`.
|
|
126
|
+
Text is required and must contain non-whitespace characters.
|
|
50
127
|
|
|
51
|
-
|
|
128
|
+
### Client options
|
|
52
129
|
|
|
53
|
-
|
|
130
|
+
These optional `new Rime({...})` fields configure TTS. They do not configure STT or Prism.
|
|
54
131
|
|
|
55
|
-
|
|
|
56
|
-
| --- | --- | --- |
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
132
|
+
| Option | Purpose and values | Required | Default |
|
|
133
|
+
| --- | --- | --- | --- |
|
|
134
|
+
| `model` | Select `"coda"` or `"mistv3"`. | No | `"coda"` |
|
|
135
|
+
| `endpoint` | Override the TTS TLS hostname and optional port, such as `"coda.api.rime.ai:443"`; no scheme or path. | No | `coda.api.rime.ai:443` for Coda; `mist.api.rime.ai:443` for Mist v3. |
|
|
136
|
+
| `timeout` | Overall TTS timeout in positive seconds, such as `60`; `null` disables it. | No | No overall timeout. |
|
|
59
137
|
|
|
60
|
-
###
|
|
138
|
+
### Synthesis options
|
|
61
139
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
| `apiKey` | `RIME_API_KEY` | API key; an explicit value overrides the environment |
|
|
65
|
-
| `model` | `"coda"` | Model for speech and discovery |
|
|
66
|
-
| `endpoint` | Model's standard endpoint | Custom hostname and optional port for speech and discovery |
|
|
67
|
-
| `timeout` | No overall deadline | Positive seconds; `null` disables the overall deadline |
|
|
140
|
+
All three TTS methods accept the same `SynthesisOptions` fields. Import
|
|
141
|
+
`AudioFormat` from `@rimelabs/sdk` when selecting an encoding.
|
|
68
142
|
|
|
69
|
-
|
|
143
|
+
| Option | Purpose and values | Required | Default |
|
|
144
|
+
| --- | --- | --- | --- |
|
|
145
|
+
| `voice` | Voice name for the selected model, such as `"clementine"` or `"astra"`. | No | `"clementine"` for Coda; `"astra"` for Mist v3. |
|
|
146
|
+
| `language` | Spoken language code, such as `"en"` or `"es"`; the service validates support. | No | `"en"` |
|
|
147
|
+
| `audioFormat` | Output encoding: `AudioFormat.PCM`, `.WAV`, `.MP3`, `.PCM_24000`, or `.MULAW_8000`. | No | Raw PCM16 at 24 kHz (`PCM_24000`). |
|
|
148
|
+
| `sampleRate` | Requested output rate in Hz: `8000`, `16000`, or `24000`. | No | `24000`; `8000` for `MULAW_8000`. |
|
|
149
|
+
| `timeScaleFactor` | Duration multiplier from `0.4` to `2.5`; below `1` speeds speech up, above `1` slows it down. | No | Omitted; normal duration (`1`). |
|
|
150
|
+
| `timestamps` | `true` requests word timings; supported only with Mist v3. | No | `false` |
|
|
151
|
+
| `customLexicon` | Array of `{ spelling, pronunciation }` entries for Coda; pronunciations use space-separated X-SAMPA strings. | No | Empty array. |
|
|
152
|
+
| `timeout` | Override the overall timeout with positive seconds, or `null` to disable it. | No | Inherits the client timeout. |
|
|
70
153
|
|
|
71
|
-
|
|
72
|
-
Omit the
|
|
73
|
-
|
|
154
|
+
Discover valid values with `await client.voices.list({ language: "en" })` and
|
|
155
|
+
`await client.languages.list()`. Omit the voice language filter to list all voices
|
|
156
|
+
for the selected model. Both calls accept an options object with `timeout` in
|
|
157
|
+
seconds; discovery has an additional ten-second maximum.
|
|
74
158
|
|
|
75
|
-
###
|
|
159
|
+
### Example: save an IVR greeting
|
|
76
160
|
|
|
77
|
-
|
|
161
|
+
Save as `tts.mjs`. This writes a complete 16 kHz WAV file, ready for playback or upload.
|
|
78
162
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
| `language` | `"en"` | Language code |
|
|
83
|
-
| `audioFormat` | `AudioFormat.PCM_24000` | Output profile, imported from `@rimelabs/sdk` |
|
|
84
|
-
| `timeout` | Client setting | Seconds; pass `null` to disable the overall deadline |
|
|
163
|
+
```javascript
|
|
164
|
+
import { writeFile } from "node:fs/promises";
|
|
165
|
+
import { AudioFormat, Rime } from "@rimelabs/sdk";
|
|
85
166
|
|
|
86
|
-
|
|
167
|
+
const client = new Rime({ timeout: 60 });
|
|
168
|
+
try {
|
|
169
|
+
const audio = await client.tts.synthesizeToBuffer(
|
|
170
|
+
"Thank you for calling. Your appointment is confirmed for tomorrow.",
|
|
171
|
+
{
|
|
172
|
+
audioFormat: AudioFormat.WAV,
|
|
173
|
+
sampleRate: 16000,
|
|
174
|
+
timeScaleFactor: 0.9,
|
|
175
|
+
},
|
|
176
|
+
);
|
|
177
|
+
await writeFile("greeting.wav", audio);
|
|
178
|
+
console.log("Saved greeting.wav");
|
|
179
|
+
} finally {
|
|
180
|
+
await client.close();
|
|
181
|
+
}
|
|
182
|
+
```
|
|
87
183
|
|
|
88
|
-
|
|
184
|
+
Run `node tts.mjs`, then open `greeting.wav` in an audio player.
|
|
185
|
+
|
|
186
|
+
### Audio and stream behavior
|
|
187
|
+
|
|
188
|
+
All output is mono. PCM is signed 16-bit little-endian audio without a header;
|
|
189
|
+
WAV contains PCM16; MP3 is encoded by the service. PCM/WAV rates are requested
|
|
190
|
+
from the server. MP3 headers determine the actual encoded rate. The fixed
|
|
191
|
+
`PCM_24000` and `MULAW_8000` presets reject conflicting sample rates; mu-law is
|
|
192
|
+
converted locally from 24 kHz PCM. Read `audio.sampleRate` on an `AudioStream`
|
|
193
|
+
for the requested rate.
|
|
194
|
+
|
|
195
|
+
The buffer helper waits for successful completion and finalizes WAV lengths.
|
|
196
|
+
Its fixed limit is **64 MiB per call**, including headers; exceeding it throws
|
|
197
|
+
`RimeResourceLimitError` without returning partial audio. WAV/MP3 stream chunks
|
|
198
|
+
are fragments of one file. Use the buffer helper when you need a complete WAV.
|
|
199
|
+
|
|
200
|
+
For incremental text, send fragments as they arrive; the service combines them
|
|
201
|
+
into sentences. Consume streaming audio to the end to confirm success, since an
|
|
202
|
+
error can follow earlier audio. Use `await audio.cancel()` when stopping early
|
|
203
|
+
and `await client.close()` in a `finally` block to release the client. Synthesis
|
|
204
|
+
is not retried automatically, and overall timeouts include pauses by the caller;
|
|
205
|
+
connection and progress limits also apply.
|
|
206
|
+
|
|
207
|
+
For timestamps, enable `timestamps: true`, consume the audio stream fully, then
|
|
208
|
+
call `await audio.timestamps()`. The result has `status` and `spans` with word
|
|
209
|
+
text and start/end seconds. Nonzero alignment status does not fail the audio.
|
|
210
|
+
The buffer helper returns only bytes, so use an audio stream to access timings.
|
|
211
|
+
Custom lexicons apply to the whole request; the service validates pronunciations
|
|
212
|
+
and model/language support.
|
|
213
|
+
|
|
214
|
+
## STT
|
|
215
|
+
|
|
216
|
+
### Transcribe a complete WAV recording
|
|
217
|
+
|
|
218
|
+
For a complete recording, use the native unary `Transcribe` RPC through the SDK.
|
|
219
|
+
Pass **complete WAV bytes, including the header**, up to **16 MiB (16,777,216
|
|
220
|
+
bytes)**. PCM16 mono 16 kHz WAV is the portable format. The SDK sends the file
|
|
221
|
+
unchanged; it does not decode, resample, or add a header. The service validates
|
|
222
|
+
its encoding and the supplied language and recognition hints.
|
|
223
|
+
|
|
224
|
+
The result is a `Transcription` containing final text, resolved language, and a
|
|
225
|
+
request ID when available. Silence may return empty text. There are no partial
|
|
226
|
+
updates or automatic retries. TTS client settings do not apply. An optional
|
|
227
|
+
overall timeout includes connection and upload; the SDK also bounds connection
|
|
228
|
+
time and the unary request (120 seconds). Timeout, cancellation, and service
|
|
229
|
+
errors use the existing SDK error categories. A request ID is included when the
|
|
230
|
+
transport has made it available.
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
import { readFile } from "node:fs/promises";
|
|
234
|
+
import { Rime } from "@rimelabs/sdk";
|
|
89
235
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
236
|
+
const client = new Rime();
|
|
237
|
+
try {
|
|
238
|
+
const result = await client.stt.transcribe(await readFile("utterance.wav"), {
|
|
239
|
+
language: "en",
|
|
240
|
+
mode: "written",
|
|
241
|
+
contextTerms: ["Super-G", "USOC"],
|
|
242
|
+
timeout: 120,
|
|
243
|
+
});
|
|
244
|
+
console.log(result.text, result.language, result.requestId);
|
|
245
|
+
} finally {
|
|
246
|
+
await client.close();
|
|
247
|
+
}
|
|
248
|
+
```
|
|
94
249
|
|
|
95
|
-
|
|
96
|
-
|
|
250
|
+
`TranscribeOptions` also accepts an `AbortSignal` as `signal`. Aborting it or
|
|
251
|
+
closing the client cancels pending requests with `RimeCancelledError`.
|
|
97
252
|
|
|
98
|
-
###
|
|
253
|
+
### Stream an utterance
|
|
99
254
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
255
|
+
Use `client.stt.stream(audioChunks, options)` to transcribe one utterance.
|
|
256
|
+
The required source is an async iterable of headerless signed PCM16 little-endian
|
|
257
|
+
`Uint8Array` chunks (`Buffer` also works). Ending the source ends the utterance;
|
|
258
|
+
your application decides when to stop.
|
|
104
259
|
|
|
105
|
-
|
|
260
|
+
STT uses gRPC over TLS to `stt.api.rime.ai:443`, with the client's API
|
|
261
|
+
key. There is no STT model, endpoint, or protocol selector. TTS client settings,
|
|
262
|
+
including its timeout, do not apply to STT.
|
|
106
263
|
|
|
107
|
-
|
|
264
|
+
### Transcription options
|
|
108
265
|
|
|
109
|
-
|
|
110
|
-
const voices = await client.voices.list({ language: "en" });
|
|
111
|
-
const languages = await client.languages.list();
|
|
112
|
-
console.log(voices, languages);
|
|
113
|
-
```
|
|
266
|
+
Pass these fields in the required `TranscriptionOptions` object.
|
|
114
267
|
|
|
115
|
-
|
|
268
|
+
| Option | Purpose and values | Required | Default |
|
|
269
|
+
| --- | --- | --- | --- |
|
|
270
|
+
| `language` | Spoken BCP-47 language tag, such as `"en"` or `"es"`; passed to the service. | Yes | None; no automatic detection. |
|
|
271
|
+
| `mode` | `"written"` normalizes written text; `"verbatim"` prefers spoken wording. | No | `"written"` |
|
|
272
|
+
| `contextTerms` | Array of recognition hints, such as `["Rime"]`; hints do not force a match. | No | Empty array. |
|
|
273
|
+
| `inputFormat` | Object with `sampleRate` (`8000`, `16000`, `24000`, `48000`), `channels` (`1` or `2`), and `encoding` (only `"pcm_s16le"`). | No | 16 kHz mono PCM16; each field is optional. |
|
|
274
|
+
| `timeout` | Overall timeout in positive seconds, including source/consumer pauses; `null` disables it. | No | No overall timeout; independent of the client timeout. |
|
|
275
|
+
| `signal` | An `AbortSignal`, such as `controller.signal`, to cancel this utterance. | No | No signal. |
|
|
116
276
|
|
|
117
|
-
|
|
277
|
+
### Example: transcribe a recording
|
|
118
278
|
|
|
119
|
-
|
|
279
|
+
Save a short recording as `utterance.pcm`: **16 kHz, mono, signed PCM16
|
|
280
|
+
little-endian, with no WAV header**. Save this as `stt.mjs`.
|
|
120
281
|
|
|
121
282
|
```javascript
|
|
122
|
-
import {
|
|
123
|
-
import { Rime
|
|
283
|
+
import { createReadStream } from "node:fs";
|
|
284
|
+
import { Rime } from "@rimelabs/sdk";
|
|
285
|
+
|
|
286
|
+
async function* audioChunks() {
|
|
287
|
+
yield* createReadStream("utterance.pcm");
|
|
288
|
+
}
|
|
124
289
|
|
|
125
|
-
|
|
290
|
+
const client = new Rime();
|
|
126
291
|
try {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
292
|
+
const transcript = client.stt.stream(audioChunks(), {
|
|
293
|
+
language: "en",
|
|
294
|
+
timeout: 120,
|
|
295
|
+
});
|
|
296
|
+
for await (const update of transcript) {
|
|
297
|
+
console.log(update.kind, update.text);
|
|
132
298
|
}
|
|
133
|
-
throw error;
|
|
134
299
|
} finally {
|
|
135
|
-
await client
|
|
300
|
+
await client.close();
|
|
136
301
|
}
|
|
137
302
|
```
|
|
138
303
|
|
|
139
|
-
Each
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
[
|
|
166
|
-
|
|
304
|
+
Run `node stt.mjs`. Each partial update replaces the **complete current
|
|
305
|
+
transcript**; do not append snapshots. A final update confirms successful
|
|
306
|
+
completion and may be empty for silence. Errors do not turn partials into finals.
|
|
307
|
+
|
|
308
|
+
The SDK converts supported input formats to 16 kHz mono. Stereo must be
|
|
309
|
+
interleaved. WAV headers, MP3, and mu-law input are not accepted. Chunks may split
|
|
310
|
+
sample frames, but an incomplete final frame fails the request. The SDK does not
|
|
311
|
+
provide microphone capture, automatic end-of-speech detection, word timings, or
|
|
312
|
+
diarization. Recognition is not retried automatically.
|
|
313
|
+
|
|
314
|
+
Use `await transcript.cancel()` or the supplied `AbortSignal` to stop one
|
|
315
|
+
utterance. Closing the client cancels all its operations. Audio sources must
|
|
316
|
+
yield without blocking the event loop. Connection, acceptance, and completion
|
|
317
|
+
limits still apply when the overall timeout is disabled.
|
|
318
|
+
|
|
319
|
+
## Help and licenses
|
|
320
|
+
|
|
321
|
+
See the [TTS guide](https://github.com/rimelabs/rime-sdk/blob/main/typescript/docs/tts.md),
|
|
322
|
+
[STT guide](https://github.com/rimelabs/rime-sdk/blob/main/typescript/docs/stt.md), and
|
|
323
|
+
[Prism guide](https://github.com/rimelabs/rime-sdk/blob/main/typescript/docs/realtime.md)
|
|
324
|
+
for more examples. Visit [Rime documentation](https://docs.rime.ai/) for service
|
|
325
|
+
information, or report SDK issues on [GitHub](https://github.com/rimelabs/rime-sdk/issues).
|
|
326
|
+
Include the SDK version and the error's `requestId` when available; omit API keys.
|
|
327
|
+
For Prism, include `session.info.requestId`.
|
|
328
|
+
|
|
329
|
+
SDK code uses the [MIT license](LICENSE). The Prism protocol module uses
|
|
330
|
+
[Apache 2.0](LICENSE-PRISM); see its [notices](NOTICE-PRISM).
|
|
331
|
+
License texts and notices are included in the distribution.
|
package/dist/auth.d.ts
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
export interface AuthenticationConfiguration {
|
|
2
|
+
exchangeUrl: string;
|
|
3
|
+
audience: string;
|
|
4
|
+
authTimeout: number;
|
|
5
|
+
}
|
|
2
6
|
export interface Token {
|
|
3
7
|
value: string;
|
|
4
8
|
expiresAt: number;
|
|
5
9
|
audience: string;
|
|
6
10
|
}
|
|
7
|
-
export declare function exchangeKey(key: string, signal: AbortSignal, configuration
|
|
11
|
+
export declare function exchangeKey(key: string, signal: AbortSignal, configuration: Readonly<AuthenticationConfiguration>): Promise<Token>;
|
|
8
12
|
export declare const authentication: {
|
|
9
13
|
exchangeKey: typeof exchangeKey;
|
|
10
14
|
};
|
|
@@ -16,7 +20,9 @@ export declare class Credentials {
|
|
|
16
20
|
private refreshAt;
|
|
17
21
|
private controller;
|
|
18
22
|
private closed;
|
|
19
|
-
constructor(key: string, configuration
|
|
23
|
+
constructor(key: string, configuration: Readonly<AuthenticationConfiguration>);
|
|
24
|
+
authorization(): string;
|
|
25
|
+
realtimeAuthorization(): string;
|
|
20
26
|
metadata(signal: AbortSignal): Promise<string>;
|
|
21
27
|
themisMetadata(signal: AbortSignal): Promise<string>;
|
|
22
28
|
close(): Promise<void>;
|
package/dist/auth.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { abortable } from "./cancellation.js";
|
|
2
2
|
import { RimeError, RimeAuthenticationError, RimePermissionError, RimeTimeoutError, RimeCancelledError, RimeResourceLimitError, RimeUnavailableError, } from "./errors.js";
|
|
3
|
-
export async function exchangeKey(key, signal, configuration
|
|
3
|
+
export async function exchangeKey(key, signal, configuration) {
|
|
4
4
|
try {
|
|
5
5
|
const response = await fetch(configuration.exchangeUrl, {
|
|
6
6
|
method: "POST",
|
|
@@ -77,17 +77,25 @@ export class Credentials {
|
|
|
77
77
|
refreshAt = 0;
|
|
78
78
|
controller = new AbortController();
|
|
79
79
|
closed = false;
|
|
80
|
-
constructor(key, configuration
|
|
80
|
+
constructor(key, configuration) {
|
|
81
81
|
this.key = key;
|
|
82
82
|
this.configuration = configuration;
|
|
83
83
|
}
|
|
84
|
+
authorization() {
|
|
85
|
+
if (this.closed)
|
|
86
|
+
throw new RimeAuthenticationError("Credentials are closed");
|
|
87
|
+
return `Bearer ${this.key}`;
|
|
88
|
+
}
|
|
89
|
+
realtimeAuthorization() {
|
|
90
|
+
return this.authorization();
|
|
91
|
+
}
|
|
84
92
|
async metadata(signal) {
|
|
85
93
|
// TEMPORARY until Themis is ready: replace this body with the call below.
|
|
86
94
|
// return this.themisMetadata(signal);
|
|
87
95
|
if (this.closed)
|
|
88
96
|
throw new RimeAuthenticationError("Credentials are closed");
|
|
89
97
|
signal.throwIfAborted();
|
|
90
|
-
return
|
|
98
|
+
return this.authorization();
|
|
91
99
|
}
|
|
92
100
|
async themisMetadata(signal) {
|
|
93
101
|
if (this.closed)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function abortable<T>(promise: PromiseLike<T>, signal: AbortSignal): Promise<T>;
|
|
2
|
+
/** A disposable deadline avoids retaining timers and caller abort listeners. */
|
|
3
|
+
export declare function scope(signals: (AbortSignal | undefined)[], seconds?: number, error?: Error): {
|
|
4
|
+
signal: AbortSignal;
|
|
5
|
+
dispose(): void;
|
|
6
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export function abortable(promise, signal) {
|
|
2
|
+
if (signal.aborted)
|
|
3
|
+
return Promise.reject(signal.reason);
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
const abort = () => {
|
|
6
|
+
signal.removeEventListener("abort", abort);
|
|
7
|
+
reject(signal.reason);
|
|
8
|
+
};
|
|
9
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
10
|
+
Promise.resolve(promise).then((value) => {
|
|
11
|
+
signal.removeEventListener("abort", abort);
|
|
12
|
+
resolve(value);
|
|
13
|
+
}, (error) => {
|
|
14
|
+
signal.removeEventListener("abort", abort);
|
|
15
|
+
reject(error);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
/** A disposable deadline avoids retaining timers and caller abort listeners. */
|
|
20
|
+
export function scope(signals, seconds, error) {
|
|
21
|
+
const controller = new AbortController();
|
|
22
|
+
const handlers = new Map();
|
|
23
|
+
for (const signal of new Set(signals)) {
|
|
24
|
+
if (!signal)
|
|
25
|
+
continue;
|
|
26
|
+
const abort = () => controller.abort(signal.reason);
|
|
27
|
+
if (signal.aborted)
|
|
28
|
+
abort();
|
|
29
|
+
else {
|
|
30
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
31
|
+
handlers.set(signal, abort);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
let timer;
|
|
35
|
+
if (seconds !== undefined) {
|
|
36
|
+
const end = performance.now() + seconds * 1000;
|
|
37
|
+
const expire = () => {
|
|
38
|
+
const remaining = end - performance.now();
|
|
39
|
+
if (remaining <= 0)
|
|
40
|
+
controller.abort(error);
|
|
41
|
+
else
|
|
42
|
+
timer = setTimeout(expire, Math.min(remaining, 2147483647));
|
|
43
|
+
};
|
|
44
|
+
expire();
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
signal: controller.signal,
|
|
48
|
+
dispose() {
|
|
49
|
+
clearTimeout(timer);
|
|
50
|
+
for (const [signal, handler] of handlers)
|
|
51
|
+
signal.removeEventListener("abort", handler);
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { AudioSource, TranscriptionOptions, TranscribeOptions, Transcription } from "./stt/types.js";
|
|
2
|
+
import type { TranscriptStream } from "./stt/stream.js";
|
|
3
|
+
import { type SynthesisOptions, type DiscoveryOptions, type VoiceListOptions } from "./tts/client.js";
|
|
4
|
+
import { type AudioStream, type TextSource } from "./tts/stream.js";
|
|
5
|
+
import type { RealtimeConnectOptions } from "./realtime/types.js";
|
|
6
|
+
import type { RealtimeSession } from "./realtime/session.js";
|
|
7
|
+
export type { SynthesisOptions, DiscoveryOptions, VoiceListOptions, } from "./tts/client.js";
|
|
3
8
|
export interface RimeOptions {
|
|
4
9
|
apiKey?: string | null;
|
|
5
10
|
/** Select "coda" (default) or "mistv3" for speech and discovery. */
|
|
@@ -7,28 +12,27 @@ export interface RimeOptions {
|
|
|
7
12
|
endpoint?: string | null;
|
|
8
13
|
timeout?: number | null;
|
|
9
14
|
}
|
|
10
|
-
export interface SynthesisOptions {
|
|
11
|
-
voice?: string | null;
|
|
12
|
-
language?: string;
|
|
13
|
-
audioFormat?: AudioFormat | null;
|
|
14
|
-
timeout?: number | null;
|
|
15
|
-
}
|
|
16
|
-
export interface DiscoveryOptions {
|
|
17
|
-
timeout?: number | null;
|
|
18
|
-
}
|
|
19
|
-
export interface VoiceListOptions extends DiscoveryOptions {
|
|
20
|
-
language?: string | null;
|
|
21
|
-
}
|
|
22
15
|
export declare class Rime {
|
|
23
|
-
private readonly
|
|
24
|
-
private
|
|
25
|
-
private
|
|
16
|
+
private readonly credentials;
|
|
17
|
+
private readonly ttsClient;
|
|
18
|
+
private readonly sttClient;
|
|
19
|
+
readonly stt: {
|
|
20
|
+
/** Transcribe complete WAV bytes. PCM16 mono 16 kHz is the portable format; maximum 16 MiB. */
|
|
21
|
+
transcribe: (audio: Uint8Array, options: TranscribeOptions) => Promise<Transcription>;
|
|
22
|
+
stream: (audio: AudioSource, options: TranscriptionOptions) => TranscriptStream;
|
|
23
|
+
};
|
|
24
|
+
private readonly realtimeClient;
|
|
25
|
+
readonly realtime: {
|
|
26
|
+
connect: (options?: RealtimeConnectOptions) => Promise<RealtimeSession>;
|
|
27
|
+
};
|
|
26
28
|
private closed;
|
|
27
|
-
private streams;
|
|
28
|
-
private discoveryControllers;
|
|
29
29
|
private closing;
|
|
30
|
-
private defaultTimeout;
|
|
31
30
|
readonly tts: {
|
|
31
|
+
/** Synthesize complete text and return streaming audio. Do not await this call. */
|
|
32
|
+
synthesize: (text: string, options?: SynthesisOptions) => AudioStream;
|
|
33
|
+
/** Await complete audio bytes, finalizing WAV lengths. Fixed output limit: 64 MiB. */
|
|
34
|
+
synthesizeToBuffer: (text: string, options?: SynthesisOptions) => Promise<Buffer>;
|
|
35
|
+
/** Supply incremental text and return streaming audio. Do not await this call. */
|
|
32
36
|
stream: (text: TextSource, options?: SynthesisOptions) => AudioStream;
|
|
33
37
|
};
|
|
34
38
|
readonly voices: {
|
|
@@ -39,9 +43,6 @@ export declare class Rime {
|
|
|
39
43
|
};
|
|
40
44
|
constructor(options?: RimeOptions);
|
|
41
45
|
private checkOpen;
|
|
42
|
-
private prepare;
|
|
43
|
-
private stream;
|
|
44
|
-
private discover;
|
|
45
46
|
close(): Promise<void>;
|
|
46
47
|
[Symbol.asyncDispose](): Promise<void>;
|
|
47
48
|
}
|