@rimelabs/sdk 0.1.0-alpha.7 → 0.1.0-alpha.9
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 +14 -0
- package/README.md +124 -42
- package/docs/realtime.md +15 -15
- package/docs/tts.md +3 -3
- package/package.json +4 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.1.0-alpha.9](https://github.com/rimelabs/rime-sdk/compare/typescript-v0.1.0-alpha.8...typescript-v0.1.0-alpha.9) (2026-10-08)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* make package quickstarts usable without repository access ([#24](https://github.com/rimelabs/rime-sdk/issues/24)) ([3a30b83](https://github.com/rimelabs/rime-sdk/commit/3a30b83ecaad1740c84d1e8c005520522cf56d26))
|
|
9
|
+
|
|
10
|
+
## [0.1.0-alpha.8](https://github.com/rimelabs/rime-sdk/compare/typescript-v0.1.0-alpha.7...typescript-v0.1.0-alpha.8) (2026-10-08)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* add voice examples and automate example dependency updates ([#22](https://github.com/rimelabs/rime-sdk/issues/22)) ([dea7044](https://github.com/rimelabs/rime-sdk/commit/dea7044831058ebee9ae536e8d822a4d7fabbd3d))
|
|
16
|
+
|
|
3
17
|
## [0.1.0-alpha.7](https://github.com/rimelabs/rime-sdk/compare/typescript-v0.1.0-alpha.6...typescript-v0.1.0-alpha.7) (2026-10-07)
|
|
4
18
|
|
|
5
19
|
|
package/README.md
CHANGED
|
@@ -1,93 +1,175 @@
|
|
|
1
1
|
# Rime SDK for Node.js
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Use Prism for conversations with audio input, typed turns, and tools.
|
|
3
|
+
Generate speech with TTS, or send speech to Prism and receive a spoken reply.
|
|
4
|
+
Both APIs use the `Rime` client. Your application handles audio capture and playback.
|
|
6
5
|
|
|
7
6
|
## Install
|
|
8
7
|
|
|
9
|
-
Requires Node.js 22 or later. This alpha package uses ESM and includes TypeScript
|
|
10
|
-
|
|
8
|
+
Requires Node.js 22 or later. This alpha package uses ESM and includes TypeScript types.
|
|
9
|
+
Run it on a server. Browser use is not supported.
|
|
11
10
|
|
|
12
11
|
```sh
|
|
13
|
-
|
|
12
|
+
mkdir rime-quickstart
|
|
13
|
+
cd rime-quickstart
|
|
14
|
+
npm init -y
|
|
15
|
+
npm install @rimelabs/sdk wavefile
|
|
14
16
|
export RIME_API_KEY="your-api-key"
|
|
15
17
|
```
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
does not load `.env` files.
|
|
19
|
+
Get a TTS API key from the [Rime dashboard](https://app.rime.ai/).
|
|
20
|
+
An explicit `new Rime({ apiKey: "..." })` overrides `RIME_API_KEY`. The SDK does not load `.env` files.
|
|
21
|
+
The commands below use a macOS or Linux shell.
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
These examples use JavaScript `.mjs` files so you can run them without a TypeScript build.
|
|
24
|
+
The additional [wavefile package](https://github.com/rochars/wavefile) reads and writes WAV files
|
|
25
|
+
for the examples. It is not required by the SDK.
|
|
21
26
|
|
|
22
|
-
|
|
27
|
+
## TTS: save speech
|
|
28
|
+
|
|
29
|
+
Save this as `tts.mjs`. It writes a playable `speech.wav` file with no audio-device dependencies.
|
|
23
30
|
|
|
24
31
|
```javascript
|
|
25
|
-
import {
|
|
32
|
+
import { writeFileSync } from "node:fs";
|
|
33
|
+
import wavefile from "wavefile";
|
|
26
34
|
import { Rime } from "@rimelabs/sdk";
|
|
27
35
|
|
|
28
|
-
const client = new Rime();
|
|
36
|
+
const client = new Rime({ timeout: 60 });
|
|
29
37
|
try {
|
|
30
|
-
const
|
|
31
|
-
await
|
|
32
|
-
|
|
38
|
+
const chunks = [];
|
|
39
|
+
for await (const chunk of client.tts.stream(
|
|
40
|
+
"Your appointment is confirmed for tomorrow.",
|
|
41
|
+
)) {
|
|
42
|
+
chunks.push(Buffer.from(chunk));
|
|
43
|
+
}
|
|
44
|
+
const pcm = Buffer.concat(chunks);
|
|
45
|
+
const samples = Array.from({ length: pcm.length / 2 }, (_, i) =>
|
|
46
|
+
pcm.readInt16LE(i * 2),
|
|
47
|
+
);
|
|
48
|
+
const wav = new wavefile.WaveFile();
|
|
49
|
+
wav.fromScratch(1, 24000, "16", samples);
|
|
50
|
+
writeFileSync("speech.wav", wav.toBuffer());
|
|
51
|
+
console.log("Saved speech.wav");
|
|
33
52
|
} finally {
|
|
34
53
|
await client.close();
|
|
35
54
|
}
|
|
36
55
|
```
|
|
37
56
|
|
|
38
|
-
|
|
39
|
-
|
|
57
|
+
Run `node tts.mjs`, then open `speech.wav` in your audio player.
|
|
58
|
+
This short example collects the audio in memory before writing the file.
|
|
59
|
+
For long responses, write audio chunks as they arrive.
|
|
60
|
+
|
|
61
|
+
Coda is the default model. Use `new Rime({ model: "mistv3" })` for Mist v3, or pass
|
|
62
|
+
`{ voice: "your-voice" }` as the second argument to `client.tts.stream(...)` to select a voice.
|
|
63
|
+
The default output is mono 24 kHz PCM16. The example adds its WAV header.
|
|
64
|
+
To supply text from an LLM, pass an async iterable of strings instead of a string.
|
|
65
|
+
The SDK handles sentence boundaries.
|
|
40
66
|
|
|
41
|
-
|
|
42
|
-
[TTS guide](https://github.com/rimelabs/rime-sdk/blob/main/typescript/docs/tts.md).
|
|
43
|
-
Runnable scripts are in the [examples](https://github.com/rimelabs/rime-sdk/blob/main/examples/README.md).
|
|
67
|
+
## Prism: send recorded speech
|
|
44
68
|
|
|
45
|
-
|
|
69
|
+
Prism needs a deployment that serves its realtime API. Obtain the endpoint,
|
|
70
|
+
credentials, and voice from your deployment operator. The TTS endpoint does not serve Prism.
|
|
71
|
+
Set `RIME_API_KEY` to the credential for that deployment if it differs from your TTS key.
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
export PRISM_URL="wss://your-prism-host/v1/realtime"
|
|
75
|
+
export PRISM_VOICE="your-deployment-voice"
|
|
76
|
+
```
|
|
46
77
|
|
|
47
|
-
|
|
48
|
-
|
|
78
|
+
You can omit `PRISM_VOICE` if the deployment has a default voice.
|
|
79
|
+
Record a short question and export it as `question.wav`, using **16 kHz, mono, signed 16-bit PCM WAV**.
|
|
80
|
+
Save the following as `prism.mjs` beside that file. It sends the recording at capture speed,
|
|
81
|
+
then sends silence so Prism can detect the end of speech. The spoken reply goes to `reply.wav`.
|
|
49
82
|
|
|
50
83
|
```javascript
|
|
84
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
85
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
86
|
+
import wavefile from "wavefile";
|
|
51
87
|
import { Rime, RimeRealtimeError } from "@rimelabs/sdk";
|
|
52
88
|
|
|
89
|
+
const input = new wavefile.WaveFile(readFileSync("question.wav"));
|
|
90
|
+
if (
|
|
91
|
+
input.container !== "RIFF" ||
|
|
92
|
+
input.fmt.audioFormat !== 1 ||
|
|
93
|
+
input.fmt.numChannels !== 1 ||
|
|
94
|
+
input.fmt.sampleRate !== 16000 ||
|
|
95
|
+
input.bitDepth !== "16"
|
|
96
|
+
) {
|
|
97
|
+
throw new Error("question.wav must be 16 kHz mono PCM16");
|
|
98
|
+
}
|
|
99
|
+
const data = Buffer.from(input.data.samples);
|
|
100
|
+
if (!data.length || data.length % 2)
|
|
101
|
+
throw new Error("question.wav has no complete PCM16 audio");
|
|
102
|
+
const endpoint = process.env.PRISM_URL;
|
|
103
|
+
if (!endpoint)
|
|
104
|
+
throw new Error("Set PRISM_URL to your deployment's /v1/realtime URL");
|
|
105
|
+
|
|
53
106
|
const client = new Rime();
|
|
54
|
-
const
|
|
107
|
+
const stop = new AbortController();
|
|
108
|
+
const deadline = setTimeout(() => {
|
|
109
|
+
stop.abort();
|
|
110
|
+
void client.close();
|
|
111
|
+
}, 60_000);
|
|
112
|
+
let sending;
|
|
55
113
|
let reading;
|
|
56
114
|
try {
|
|
57
115
|
const session = await client.realtime.connect({
|
|
58
|
-
endpoint
|
|
116
|
+
endpoint,
|
|
59
117
|
voice: process.env.PRISM_VOICE,
|
|
118
|
+
instructions: "Give short, clear answers.",
|
|
60
119
|
});
|
|
120
|
+
sending = (async () => {
|
|
121
|
+
for (let offset = 0; ; offset += 1280) {
|
|
122
|
+
const frame = Buffer.alloc(1280);
|
|
123
|
+
if (offset < data.length)
|
|
124
|
+
data.copy(frame, 0, offset, Math.min(offset + 1280, data.length));
|
|
125
|
+
await session.sendAudio({ data: frame });
|
|
126
|
+
await sleep(40, undefined, { signal: stop.signal });
|
|
127
|
+
}
|
|
128
|
+
})();
|
|
129
|
+
const chunks = [];
|
|
61
130
|
reading = (async () => {
|
|
62
131
|
for await (const { payload } of session.events) {
|
|
63
|
-
if (payload.kind === "
|
|
132
|
+
if (payload.kind === "audio.delta")
|
|
133
|
+
chunks.push(Buffer.from(payload.audio.data));
|
|
64
134
|
if (payload.kind === "error") throw new RimeRealtimeError(payload.error);
|
|
65
135
|
if (payload.kind === "response.ended") {
|
|
66
|
-
if (payload.status !== "completed")
|
|
67
|
-
throw new Error(`Response ${payload.status}`);
|
|
136
|
+
if (payload.status !== "completed") {
|
|
137
|
+
throw new Error(`Response ${payload.status}: ${payload.reason}`);
|
|
138
|
+
}
|
|
68
139
|
return;
|
|
69
140
|
}
|
|
70
141
|
}
|
|
71
|
-
throw new Error("Session closed before the
|
|
142
|
+
throw new Error("Session closed before the reply ended");
|
|
72
143
|
})();
|
|
73
|
-
await Promise.
|
|
144
|
+
await Promise.race([sending, reading]);
|
|
145
|
+
const pcm = Buffer.concat(chunks);
|
|
146
|
+
const samples = Array.from({ length: pcm.length / 2 }, (_, i) =>
|
|
147
|
+
pcm.readInt16LE(i * 2),
|
|
148
|
+
);
|
|
149
|
+
const wav = new wavefile.WaveFile();
|
|
150
|
+
wav.fromScratch(1, 24000, "16", samples);
|
|
151
|
+
writeFileSync("reply.wav", wav.toBuffer());
|
|
152
|
+
console.log("Saved reply.wav");
|
|
74
153
|
} finally {
|
|
154
|
+
stop.abort();
|
|
75
155
|
clearTimeout(deadline);
|
|
76
156
|
await client.close();
|
|
77
|
-
await
|
|
157
|
+
await Promise.allSettled([sending, reading]);
|
|
78
158
|
}
|
|
79
159
|
```
|
|
80
160
|
|
|
81
|
-
This example
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
161
|
+
Run `node prism.mjs`, then open `reply.wav`. This example has a 60-second deadline
|
|
162
|
+
and collects the reply in memory. It saves audio without playing it, so it sends no playback receipt.
|
|
163
|
+
For live conversations, your application must capture microphone audio, play replies,
|
|
164
|
+
stop playback on interruption, and report the actual played position to Prism.
|
|
165
|
+
Keep the API key on your server when you add a browser interface.
|
|
166
|
+
TTS settings do not configure Prism sessions.
|
|
167
|
+
|
|
168
|
+
## Help and licenses
|
|
86
169
|
|
|
87
|
-
|
|
170
|
+
See [Rime documentation](https://docs.rime.ai/) for API keys, TTS models, and deployment information.
|
|
171
|
+
The examples above run with the installed package. No source checkout is required.
|
|
88
172
|
|
|
89
|
-
SDK code uses the
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
Its WASM runtime ships with the package; the SDK downloads no code or models at
|
|
93
|
-
runtime. See [vendor provenance](https://github.com/rimelabs/rime-sdk/blob/main/typescript/vendor/README.md).
|
|
173
|
+
SDK code uses the MIT license. The Prism protocol module uses Apache 2.0.
|
|
174
|
+
BlingFire retains its MIT license. License texts and notices are included in the package.
|
|
175
|
+
Its WASM runtime ships with the package. The SDK downloads no code or models at runtime.
|
package/docs/realtime.md
CHANGED
|
@@ -4,10 +4,12 @@ Keep one event consumer running while your application sends input, runs tools,
|
|
|
4
4
|
and plays audio. The SDK manages the protocol state. Your application owns
|
|
5
5
|
microphone capture, tool execution, and playback.
|
|
6
6
|
|
|
7
|
-
Start with the [
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
Start with the [recorded-speech quick start](../README.md#prism-send-recorded-speech)
|
|
8
|
+
to save a spoken reply without audio hardware.
|
|
9
|
+
|
|
10
|
+
For live conversations, keep the session open after normal cancellation and
|
|
11
|
+
report playback from the output clock. Saving a file is not playback.
|
|
12
|
+
Typed turns also support mixed text and voice applications.
|
|
11
13
|
|
|
12
14
|
## Connect
|
|
13
15
|
|
|
@@ -106,10 +108,9 @@ Use `JSON.stringify(...)` for a structured result. Call
|
|
|
106
108
|
`continueReply(payload.call)` when the assistant should answer; a tool call
|
|
107
109
|
reference also carries its parent response identity.
|
|
108
110
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
so they do not block the Node.js event loop.
|
|
111
|
+
Keep the event consumer running while submitting tool results and requesting
|
|
112
|
+
continuation. Run slow tools asynchronously or in a worker so they do not block
|
|
113
|
+
the event loop.
|
|
113
114
|
|
|
114
115
|
A new user turn or proactive reply supersedes the old tool round. Late results
|
|
115
116
|
can still be recorded. Use `requestReply({ toolCall: call })` to ask the assistant
|
|
@@ -174,8 +175,8 @@ nonnegative milliseconds. The SDK waits for generation completion and supplies
|
|
|
174
175
|
the server's drain token.
|
|
175
176
|
|
|
176
177
|
Saving audio does not establish playback. Reports are optional. If you cannot
|
|
177
|
-
measure playback, omit them and let the server use its estimate. The examples
|
|
178
|
-
|
|
178
|
+
measure playback, omit them and let the server use its estimate. The recorded and typed examples have no player and omit these reports.
|
|
179
|
+
The voice examples report actual playback.
|
|
179
180
|
|
|
180
181
|
## Timeouts and failures
|
|
181
182
|
|
|
@@ -185,8 +186,8 @@ tool readiness, and waiting for generation to end before a playback report.
|
|
|
185
186
|
Once a request is submitted, `requestS` bounds its write and acknowledgment.
|
|
186
187
|
|
|
187
188
|
These limits do not bound the entire conversation or generation after creation.
|
|
188
|
-
Set an application deadline and close the session when it expires.
|
|
189
|
-
|
|
189
|
+
Set an application deadline and close the session when it expires. The recorded and typed examples use a 60-second deadline. Voice conversations
|
|
190
|
+
run until stopped or the connection fails.
|
|
190
191
|
|
|
191
192
|
`RealtimeAdmissionTimeout` means readiness expired with no request outstanding;
|
|
192
193
|
the session remains usable. An unknown request outcome closes the connection and
|
|
@@ -215,6 +216,5 @@ Keep up with the event stream. The queue holds 256 events; incoming messages
|
|
|
215
216
|
are limited to 1 MiB. A slow consumer ends the session. There is no automatic
|
|
216
217
|
reconnect, history replay, or live configuration update.
|
|
217
218
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
still need validation for the deployment you use.
|
|
219
|
+
Local peer tests cover the SDK protocol handling. Validate the gateway handshake
|
|
220
|
+
and endpoint behavior for the deployment you use.
|
package/docs/tts.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# TTS for Node.js
|
|
2
2
|
|
|
3
3
|
You supply text; the SDK handles sentence boundaries and streams audio back.
|
|
4
|
-
Your application owns playback. Start with the
|
|
5
|
-
|
|
4
|
+
Your application owns playback. Start with the complete WAV example in the
|
|
5
|
+
[package quick start](../README.md#tts-save-speech).
|
|
6
6
|
|
|
7
7
|
## Stream incoming text
|
|
8
8
|
|
|
@@ -17,7 +17,7 @@ async function* text() {
|
|
|
17
17
|
|
|
18
18
|
Use `client.tts.stream(text())` with the same file write and cleanup.
|
|
19
19
|
The SDK handles sentence boundaries. Do not await `tts.stream()`; the first
|
|
20
|
-
iterator read starts work.
|
|
20
|
+
iterator read starts work. The quick start collects chunks before writing a WAV file.
|
|
21
21
|
|
|
22
22
|
## Configuration
|
|
23
23
|
|
package/package.json
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rimelabs/sdk",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.9",
|
|
4
4
|
"description": "Rime streaming TTS and Prism realtime SDK for Node.js",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"repository": {
|
|
7
|
-
"type": "git",
|
|
8
|
-
"url": "git+https://github.com/rimelabs/rime-sdk.git",
|
|
9
|
-
"directory": "typescript"
|
|
10
|
-
},
|
|
11
6
|
"type": "module",
|
|
12
7
|
"engines": {
|
|
13
8
|
"node": ">=22"
|
|
@@ -34,7 +29,7 @@
|
|
|
34
29
|
"check": "tsc -p tsconfig.json --noEmit",
|
|
35
30
|
"test": "npm run build && node --test tests/*.test.mjs",
|
|
36
31
|
"prepack": "npm run build",
|
|
37
|
-
"lint": "prettier --check src tests ../examples/typescript/tts ../examples/typescript/realtime"
|
|
32
|
+
"lint": "prettier --check src tests ../examples/typescript/tts ../examples/typescript/realtime ../examples/typescript/common ../examples/typescript/browser ../examples/typescript/tests"
|
|
38
33
|
},
|
|
39
34
|
"dependencies": {
|
|
40
35
|
"@bufbuild/protobuf": "^2.11.0",
|
|
@@ -49,5 +44,6 @@
|
|
|
49
44
|
"prettier": "3.9.8",
|
|
50
45
|
"typescript": "^5.9.0",
|
|
51
46
|
"yaml": "^2.9.1"
|
|
52
|
-
}
|
|
47
|
+
},
|
|
48
|
+
"homepage": "https://docs.rime.ai/"
|
|
53
49
|
}
|