@rimelabs/sdk 0.1.0-alpha.5 → 0.1.0-alpha.7

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.
Files changed (43) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/LICENSE-PRISM +201 -0
  3. package/NOTICE-PRISM +9 -0
  4. package/README.md +50 -123
  5. package/dist/auth.d.ts +8 -3
  6. package/dist/auth.js +8 -3
  7. package/dist/cancellation.d.ts +1 -0
  8. package/dist/cancellation.js +18 -0
  9. package/dist/client.d.ts +11 -23
  10. package/dist/client.js +22 -91
  11. package/dist/index.d.ts +4 -2
  12. package/dist/index.js +4 -2
  13. package/dist/realtime/async.d.ts +27 -0
  14. package/dist/realtime/async.js +122 -0
  15. package/dist/realtime/audio.d.ts +13 -0
  16. package/dist/realtime/audio.js +54 -0
  17. package/dist/realtime/client.d.ts +12 -0
  18. package/dist/realtime/client.js +156 -0
  19. package/dist/realtime/protocol.d.ts +80 -0
  20. package/dist/realtime/protocol.js +354 -0
  21. package/dist/realtime/session.d.ts +67 -0
  22. package/dist/realtime/session.js +730 -0
  23. package/dist/realtime/transport.d.ts +16 -0
  24. package/dist/realtime/transport.js +83 -0
  25. package/dist/realtime/types.d.ts +190 -0
  26. package/dist/realtime/types.js +10 -0
  27. package/dist/{audio.js → tts/audio.js} +1 -1
  28. package/dist/tts/client.d.ts +36 -0
  29. package/dist/tts/client.js +111 -0
  30. package/dist/{policy.d.ts → tts/policy.d.ts} +0 -1
  31. package/dist/{policy.js → tts/policy.js} +1 -19
  32. package/dist/{sentences.js → tts/sentences.js} +2 -2
  33. package/dist/{stream.js → tts/stream.js} +3 -2
  34. package/dist/{transport.d.ts → tts/transport.d.ts} +1 -1
  35. package/dist/{transport.js → tts/transport.js} +3 -2
  36. package/docs/realtime.md +220 -0
  37. package/docs/tts.md +132 -0
  38. package/package.json +12 -5
  39. /package/dist/{audio.d.ts → tts/audio.d.ts} +0 -0
  40. /package/dist/{queue.d.ts → tts/queue.d.ts} +0 -0
  41. /package/dist/{queue.js → tts/queue.js} +0 -0
  42. /package/dist/{sentences.d.ts → tts/sentences.d.ts} +0 -0
  43. /package/dist/{stream.d.ts → tts/stream.d.ts} +0 -0
@@ -0,0 +1,220 @@
1
+ # Realtime / Prism for Node.js
2
+
3
+ Keep one event consumer running while your application sends input, runs tools,
4
+ and plays audio. The SDK manages the protocol state. Your application owns
5
+ microphone capture, tool execution, and playback.
6
+
7
+ Start with the [package quick start](../README.md#prism-quick-start), or run the
8
+ [typed-turn example](https://github.com/rimelabs/rime-sdk/blob/main/examples/typescript/realtime/typed-turn.mjs).
9
+ The [example setup](https://github.com/rimelabs/rime-sdk/blob/main/examples/README.md#nodejs)
10
+ lists the commands and environment variables. This API requires Node.js 22 or later.
11
+
12
+ ## Connect
13
+
14
+ Use `await client.realtime.connect(options)`. It opens a WebSocket, initializes
15
+ one conversation, and returns a `RealtimeSession`. Settings apply once. Open a
16
+ new session to change them.
17
+
18
+ | Option | Required? | Default and behavior |
19
+ | --- | --- | --- |
20
+ | `endpoint` | Yes | Full `wss://host/v1/realtime` URL. Plain `ws://` is allowed only for localhost. |
21
+ | `voice` | If the server has no default voice | Omitted or `null` uses the server default. Supplied strings must be nonblank. |
22
+ | `instructions` | No | Omitted or `null` uses server defaults. Supplied strings must be nonblank. |
23
+ | `tools` | No | `[]`. Each tool requires `name` and a JSON Schema `parameters` object. `description` defaults to `""`. |
24
+ | `interruptOnSpeech` | No | `true`. Requests interruption when the server confirms user speech. |
25
+ | `timeouts` | No | `{ connectS: 10, readyS: 30, requestS: 10 }`. Each field is optional. Units are seconds. |
26
+ | `model` | No | `"prism"`, the only accepted value. |
27
+ | `signal` | No | An `AbortSignal` that cancels connection setup. It does not cancel an established session. |
28
+
29
+ Credentials come from `new Rime({ apiKey: "..." })` or `RIME_API_KEY`. Prism uses
30
+ `Authorization: Bearer <key>`, without a TTS token exchange. TTS endpoint, model,
31
+ voice, and timeout settings do not apply to Realtime.
32
+
33
+ After connection, `session.info` contains `sessionId`, `model`, `voice`,
34
+ `interruptOnSpeech`, `toolResultTimeoutS`, and `toolContinuationTimeoutS`.
35
+ Check the effective interruption setting there. Tool wait durations are total
36
+ server deadlines, not remaining time. A `null` duration can mean an omitted
37
+ field; it does not prove that no deadline exists.
38
+
39
+ Close the session in `finally` with `await session.close()`. Closing the parent
40
+ client closes all its TTS and Realtime operations. Both objects also support
41
+ `Symbol.asyncDispose`. Repeated close calls are safe.
42
+
43
+ ## Choose an operation
44
+
45
+ Every method returns a promise. Arguments without `?` are required.
46
+
47
+ | Method | Result and behavior |
48
+ | --- | --- |
49
+ | `sendText(text, options?)` | Starts a typed user turn after readiness; returns `ResponseRef` at response creation. |
50
+ | `sendAudio(chunk, options?)` | Sends microphone audio with transport backpressure. |
51
+ | `requestReply(options?)` | Requests a reply without new user input; returns `ResponseRef`. Options can include `instruction` and `toolCall`. |
52
+ | `addMessage(role, text, options?)` | Adds user or assistant history without a reply; returns `ItemRef` after acknowledgment. |
53
+ | `submitToolResult(call, output, options?)` | Records a string result and waits for acknowledgment. |
54
+ | `continueReply(parent, options?)` | Waits for parent completion and all tool result acknowledgments, then returns a new `ResponseRef`. |
55
+ | `cancel(response, options?)` | Requests generation cancellation and waits for a terminal response or refusal. |
56
+ | `clearAudio(options?)` | Clears buffered input and resets audio conversion after acknowledgment. |
57
+ | `reportPlayback(report, options?)` | Reports interrupted or finished playback. |
58
+ | `close()` | Closes the session and releases its resources. |
59
+
60
+ Operation options accept `signal?: AbortSignal`. `requestReply` accepts its
61
+ instruction, tool call, and signal in one object. Methods other than `sendText`,
62
+ `requestReply`, `addMessage`, and `continueReply` return `Promise<void>`.
63
+
64
+ Text must be nonblank and contain at most 4,000 Unicode characters. History
65
+ roles are `"user"` and `"assistant"`. A reply instruction applies to one response;
66
+ it does not replace session instructions.
67
+
68
+ A returned `ResponseRef` means generation started. Read `response.ended` for the
69
+ terminal status. Generation can finish before cancellation takes effect, so a
70
+ successful `cancel()` does not guarantee a `"cancelled"` status.
71
+
72
+ ## Send audio
73
+
74
+ Pass a plain object with `data: Uint8Array`. Node.js `Buffer` is also accepted.
75
+ The optional `format` object defaults to 16 kHz mono signed little-endian PCM16.
76
+
77
+ ```typescript
78
+ await session.sendAudio({
79
+ data: pcmBytes,
80
+ format: { sampleRate: 48000, channels: 2 },
81
+ });
82
+ ```
83
+
84
+ Supported `sampleRate` values are `8000`, `16000`, `24000`, and `48000`.
85
+ Channels can be `1` or `2`; `encoding` can only be `"pcm_s16le"`. Each field in
86
+ `format` is optional. Chunks must contain complete sample frames and at most
87
+ 192,000 bytes. The SDK downmixes and resamples to 16 kHz mono, retaining state
88
+ across calls. A format change or acknowledged `clearAudio()` resets that state.
89
+
90
+ Await each send and bound your capture queue. Keep audio flowing, including
91
+ silence and while the assistant speaks or tools run. Prism detects and commits
92
+ speech; there is no manual commit method. Echo cancellation belongs to your app.
93
+
94
+ Received `audio.delta` events carry 24 kHz mono PCM16. The outgoing chunk limit
95
+ does not apply to received audio.
96
+
97
+ ## Handle tools
98
+
99
+ Declare tools with plain `ToolDefinition` objects. Names must be nonblank and
100
+ unique; schemas must contain finite JSON values. The SDK copies schemas before
101
+ connection setup, so later caller changes cannot alter initialization.
102
+
103
+ Execute a tool once for each `tool.call` event. Validate its parsed `arguments`,
104
+ then submit a string result with `submitToolResult(payload.call, output)`.
105
+ Use `JSON.stringify(...)` for a structured result. Call
106
+ `continueReply(payload.call)` when the assistant should answer; a tool call
107
+ reference also carries its parent response identity.
108
+
109
+ The [tool example](https://github.com/rimelabs/rime-sdk/blob/main/examples/typescript/realtime/tools.mjs)
110
+ keeps the event consumer running while result submissions and continuation wait.
111
+ It uses local order data. Slow tools need asynchronous work or a worker thread
112
+ so they do not block the Node.js event loop.
113
+
114
+ A new user turn or proactive reply supersedes the old tool round. Late results
115
+ can still be recorded. Use `requestReply({ toolCall: call })` to ask the assistant
116
+ to report a recorded result. Repeating the same result is safe within the live
117
+ session; changing it is rejected. Do not rerun a tool because delivery failed.
118
+
119
+ ## Read events and references
120
+
121
+ Use `for await (const event of session.events)`. Each `SessionEvent` has
122
+ `sessionId`, `eventId`, nullable `requestId`, and a `payload`. Switch on
123
+ `payload.kind` to narrow the TypeScript union. All public types and error classes
124
+ are exported from `@rimelabs/sdk`.
125
+
126
+ | Kind | Payload fields |
127
+ | --- | --- |
128
+ | `input.ready` | No data fields. Turn methods handle the readiness wait. |
129
+ | `speech.started`, `speech.stopped` | `itemId` and `audioStartMs` or `audioEndMs` |
130
+ | `input.committed` | `itemId` |
131
+ | `transcript.delta`, `transcript.final` | `itemId` and `delta` or `text` |
132
+ | `transcript.failed` | `itemId`, `error` |
133
+ | `response.started` | `response`, `cause`, nullable `parent` and `inputItemId` |
134
+ | `message.started` | `output` |
135
+ | `text.delta`, `text.done` | `output` and `delta` or `text` |
136
+ | `audio.delta`, `audio.done` | `output`; a delta also has `audio.data` and a complete `audio.format` |
137
+ | `tool.call` | `call`, `itemId`, `name`, parsed `arguments` |
138
+ | `response.ended` | `response`, `status`, nullable `reason` |
139
+ | `response.abandoned` | `response`; the SDK requests cancellation after the requesting caller aborted. |
140
+ | `error` | `error`, a `RealtimeFault` |
141
+
142
+ References come from the SDK. Pass them back to identify the object you mean.
143
+
144
+ | Reference | Fields |
145
+ | --- | --- |
146
+ | `ResponseRef` | `sessionId`, `responseId` |
147
+ | `ItemRef` | `sessionId`, `itemId` |
148
+ | `ToolCallRef` | `sessionId`, `responseId`, `callId` |
149
+ | `OutputRef` | `response`, `itemId`, `outputIndex`, `contentIndex` |
150
+
151
+ Every reference belongs to one session. Only the last 128 responses are retained;
152
+ older references expire. Current output uses `contentIndex: 0`.
153
+
154
+ Unknown event kinds and extra fields are ignored. Unknown response causes,
155
+ terminal statuses, and error scopes become `"unknown"`. An absent response cause
156
+ means speech. The local `response.abandoned` event has an empty `eventId` and
157
+ `requestId: null`; it can follow `response.ended` when completion races cancellation.
158
+
159
+ ## Report playback
160
+
161
+ Generation completion and playback completion are separate. Report the player's
162
+ actual state, including output latency and queued audio.
163
+
164
+ | Report object | Required fields | Optional fields |
165
+ | --- | --- | --- |
166
+ | Interrupted | `kind: "interrupted"`, `output`, `audioEndMs` | None |
167
+ | Finished | `kind: "finished"`, `response` | `playedMs`, `audibleTailMs`; both default to `null` |
168
+
169
+ Pass the object to `await session.reportPlayback(report)`. Interruption records
170
+ the point where caller speech stopped an assistant message. `audioEndMs` must
171
+ be an integer from 0 through 4,294,967,295. Finished playback means all queued
172
+ audio has played or has permanently stopped. Optional durations must be finite,
173
+ nonnegative milliseconds. The SDK waits for generation completion and supplies
174
+ the server's drain token.
175
+
176
+ 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
+ have no player and omit these reports.
179
+
180
+ ## Timeouts and failures
181
+
182
+ Configure positive, finite seconds through `timeouts`. `connectS` bounds the
183
+ WebSocket handshake and initial session event. `readyS` bounds turn admission,
184
+ tool readiness, and waiting for generation to end before a playback report.
185
+ Once a request is submitted, `requestS` bounds its write and acknowledgment.
186
+
187
+ These limits do not bound the entire conversation or generation after creation.
188
+ Set an application deadline and close the session when it expires. Both examples
189
+ use a 60-second deadline.
190
+
191
+ `RealtimeAdmissionTimeout` means readiness expired with no request outstanding;
192
+ the session remains usable. An unknown request outcome closes the connection and
193
+ is never replayed. The SDK retries typed-turn admission only after named refusals
194
+ that confirm the request made no change.
195
+
196
+ Protocol faults appear as `error` events and can also reject an affected method
197
+ with `RimeRealtimeError`. Its `.fault` has `code`, `message`, `scope`, nullable
198
+ `requestId`, `responseId`, `itemId`, `correlationId`, `parameter`, and a `callIds`
199
+ array. A response fault does not itself end generation. Session faults and lost
200
+ connections end the session.
201
+
202
+ Malformed fields in known events and mismatched request acknowledgments close
203
+ the session with `RimeStreamError`. Unknown event kinds and extra fields remain
204
+ accepted and ignored. The SDK does not replay a request after a protocol failure.
205
+
206
+ Aborting an operation rejects it with `RimeCancelledError`. For reply requests,
207
+ the SDK prevents an unsent request or cancels a late accepted response. Control
208
+ operations can wait for acknowledgment before rejecting, to keep acknowledgment
209
+ matching correct. A submitted tool result keeps matching its acknowledgment even
210
+ if its caller aborts. Aborting audio after submission starts closes the session;
211
+ before submission, it restores conversion state. Do not resend a chunk with an
212
+ unknown outcome.
213
+
214
+ Keep up with the event stream. The queue holds 256 events; incoming messages
215
+ are limited to 1 MiB. A slow consumer ends the session. There is no automatic
216
+ reconnect, history replay, or live configuration update.
217
+
218
+ The [pinned contract](https://github.com/rimelabs/rime-sdk/blob/main/conformance/prism/README.md)
219
+ is the local test target. The deployed gateway handshake and endpoint behavior
220
+ still need validation for the deployment you use.
package/docs/tts.md ADDED
@@ -0,0 +1,132 @@
1
+ # TTS for Node.js
2
+
3
+ You supply text; the SDK handles sentence boundaries and streams audio back.
4
+ Your application owns playback. Start with the [package quick start](../README.md),
5
+ or run the [streaming example](https://github.com/rimelabs/rime-sdk/blob/main/examples/typescript/tts/stream.mjs).
6
+
7
+ ## Stream incoming text
8
+
9
+ An async text source can supply text as it arrives:
10
+
11
+ ```javascript
12
+ async function* text() {
13
+ yield "Hello. ";
14
+ yield "This text arrives in separate chunks.";
15
+ }
16
+ ```
17
+
18
+ Use `client.tts.stream(text())` with the same file write and cleanup.
19
+ The SDK handles sentence boundaries. Do not await `tts.stream()`; the first
20
+ iterator read starts work. `writeFile` consumes the stream for you.
21
+
22
+ ## Configuration
23
+
24
+ Select Mist v3 with `new Rime({ model: "mistv3" })`. Mist v1 and v2 are not supported.
25
+
26
+ | Model | Standard endpoint | Default voice |
27
+ | --- | --- | --- |
28
+ | `coda` | `coda.api.rime.ai:443` | `clementine` |
29
+ | `mistv3` | `mist.api.rime.ai:443` | `astra` |
30
+
31
+ ### Client options
32
+
33
+ | `new Rime({ ... })` option | Default | Meaning |
34
+ | --- | --- | --- |
35
+ | `apiKey` | `RIME_API_KEY` | API key; an explicit value overrides the environment |
36
+ | `model` | `"coda"` | Model for speech and discovery |
37
+ | `endpoint` | Model's standard endpoint | Custom hostname and optional port for speech and discovery |
38
+ | `timeout` | No overall deadline | Positive seconds; `null` disables the overall deadline |
39
+
40
+ Authentication uses bearer tokens over TLS. The SDK does not load `.env` files.
41
+
42
+ Custom deployments use `new Rime({ model: "coda", endpoint: "host:8443" })`.
43
+ Omit the scheme and path. TLS is required; the default port is `443`.
44
+ Model defaults still apply.
45
+
46
+ ### Synthesis options
47
+
48
+ The `text` argument is required and accepts a nonblank string or an async text
49
+ source. All options in `client.tts.stream(text, { ... })` are optional:
50
+
51
+ | Option | Default | Meaning |
52
+ | --- | --- | --- |
53
+ | `voice` | Model's default voice | Voice name from `client.voices.list()` |
54
+ | `language` | `"en"` | Language code |
55
+ | `audioFormat` | `AudioFormat.PCM_24000` | Output profile, imported from `@rimelabs/sdk` |
56
+ | `timeout` | Client setting | Seconds; pass `null` to disable the overall deadline |
57
+
58
+ ### Audio formats
59
+
60
+ Both profiles return raw audio without a file header.
61
+
62
+ | `AudioFormat` profile | Encoding | Sample rate | Channels |
63
+ | --- | --- | --- | --- |
64
+ | `PCM_24000` | Signed 16-bit little-endian PCM | 24 kHz | Mono |
65
+ | `MULAW_8000` | G.711 mu-law | 8 kHz | Mono |
66
+
67
+ Chunks are `Uint8Array` values with complete sample frames; sizes vary. Inspect the
68
+ read-only `audio.format.encoding`, `audio.format.sampleRate`, and `audio.format.channels`.
69
+
70
+ ### Timeouts
71
+
72
+ An overall deadline includes pauses between reads. Disabling it leaves internal
73
+ connection and progress limits active. Discovery allows at most 10 seconds;
74
+ a shorter supplied timeout takes precedence. Omit an operation's `timeout` or
75
+ pass `undefined` to inherit the client setting.
76
+
77
+ ## Discover voices and languages
78
+
79
+ List voices and languages while the client is open:
80
+
81
+ ```javascript
82
+ const voices = await client.voices.list({ language: "en" });
83
+ const languages = await client.languages.list();
84
+ console.log(voices, languages);
85
+ ```
86
+
87
+ Both return string arrays and accept `timeout`. Omit `language` to disable filtering.
88
+
89
+ ## Errors and cancellation
90
+
91
+ Report SDK errors and request IDs while closing the client on failure:
92
+
93
+ ```javascript
94
+ import { writeFile } from "node:fs/promises";
95
+ import { Rime, RimeError } from "@rimelabs/sdk";
96
+
97
+ let client;
98
+ try {
99
+ client = new Rime();
100
+ await writeFile("speech.pcm", client.tts.stream("Hello. This is Rime."));
101
+ } catch (error) {
102
+ if (error instanceof RimeError) {
103
+ console.error(`Speech failed: ${error.message}; requestId=${error.requestId}`);
104
+ }
105
+ throw error;
106
+ } finally {
107
+ await client?.close();
108
+ }
109
+ ```
110
+
111
+ Each SDK error inherits from `RimeError`:
112
+
113
+ | Error | Meaning |
114
+ | --- | --- |
115
+ | `RimeAuthenticationError` | Missing or rejected credentials |
116
+ | `RimePermissionError` | Access denied |
117
+ | `RimeInputError` | Invalid input or client use |
118
+ | `RimeResourceLimitError` | Service resource limit reached |
119
+ | `RimeUnavailableError` | Service unavailable |
120
+ | `RimeTimeoutError` | Overall or internal deadline reached |
121
+ | `RimeAudioFormatError` | Unsupported or unexpected audio format |
122
+ | `RimeCancelledError` | Operation cancelled |
123
+ | `RimeStreamError` | Other stream or transport failure |
124
+
125
+ Partial audio can arrive before an error. Output is complete only after successful
126
+ iteration. Synthesis is not retried automatically.
127
+
128
+ Keep cleanup when stopping early. A `for await` loop exit cancels the stream.
129
+ For manual reads, call `await audio.cancel()` in `finally`. `await client.close()`
130
+ stops all client work. Both calls are safe to repeat; both objects support
131
+ `Symbol.asyncDispose`. Cancellation stops the RPC and limits cleanup time, but
132
+ cannot force your text source to finish an unrelated promise.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rimelabs/sdk",
3
- "version": "0.1.0-alpha.5",
4
- "description": "Rime streaming text-to-speech SDK for Node.js",
3
+ "version": "0.1.0-alpha.7",
4
+ "description": "Rime streaming TTS and Prism realtime SDK for Node.js",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -21,8 +21,11 @@
21
21
  },
22
22
  "files": [
23
23
  "dist",
24
+ "docs",
24
25
  "vendor",
25
26
  "LICENSE",
27
+ "LICENSE-PRISM",
28
+ "NOTICE-PRISM",
26
29
  "README.md",
27
30
  "CHANGELOG.md"
28
31
  ],
@@ -31,16 +34,20 @@
31
34
  "check": "tsc -p tsconfig.json --noEmit",
32
35
  "test": "npm run build && node --test tests/*.test.mjs",
33
36
  "prepack": "npm run build",
34
- "lint": "prettier --check src tests"
37
+ "lint": "prettier --check src tests ../examples/typescript/tts ../examples/typescript/realtime"
35
38
  },
36
39
  "dependencies": {
37
40
  "@bufbuild/protobuf": "^2.11.0",
38
41
  "@grpc/grpc-js": "^1.14.0",
39
- "@rimelabs/api": "0.1.0"
42
+ "@rimelabs/api": "0.1.0",
43
+ "ws": "^8.22.0"
40
44
  },
41
45
  "devDependencies": {
42
46
  "@types/node": "^22.0.0",
47
+ "@types/ws": "^8.18.2",
48
+ "ajv": "^8.20.0",
43
49
  "prettier": "3.9.8",
44
- "typescript": "^5.9.0"
50
+ "typescript": "^5.9.0",
51
+ "yaml": "^2.9.1"
45
52
  }
46
53
  }
File without changes
File without changes
File without changes
File without changes
File without changes