@rimelabs/sdk 0.1.0-alpha.1 → 0.1.0-alpha.12

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 (67) hide show
  1. package/CHANGELOG.md +96 -11
  2. package/LICENSE-PRISM +201 -0
  3. package/NOTICE-PRISM +9 -0
  4. package/README.md +175 -63
  5. package/dist/auth.d.ts +9 -3
  6. package/dist/auth.js +12 -4
  7. package/dist/cancellation.d.ts +6 -0
  8. package/dist/cancellation.js +54 -0
  9. package/dist/client.d.ts +23 -23
  10. package/dist/client.js +39 -92
  11. package/dist/grpc.d.ts +27 -0
  12. package/dist/{transport.js → grpc.js} +36 -106
  13. package/dist/index.d.ts +6 -2
  14. package/dist/index.js +5 -2
  15. package/dist/pcm.d.ts +21 -0
  16. package/dist/pcm.js +52 -0
  17. package/dist/realtime/async.d.ts +23 -0
  18. package/dist/realtime/async.js +87 -0
  19. package/dist/realtime/audio.d.ts +2 -0
  20. package/dist/realtime/audio.js +11 -0
  21. package/dist/realtime/client.d.ts +12 -0
  22. package/dist/realtime/client.js +156 -0
  23. package/dist/realtime/protocol.d.ts +80 -0
  24. package/dist/realtime/protocol.js +354 -0
  25. package/dist/realtime/session.d.ts +67 -0
  26. package/dist/realtime/session.js +731 -0
  27. package/dist/realtime/transport.d.ts +16 -0
  28. package/dist/realtime/transport.js +83 -0
  29. package/dist/realtime/types.d.ts +186 -0
  30. package/dist/realtime/types.js +10 -0
  31. package/dist/stt/audio.d.ts +15 -0
  32. package/dist/stt/audio.js +46 -0
  33. package/dist/stt/client.d.ts +13 -0
  34. package/dist/stt/client.js +73 -0
  35. package/dist/stt/policy.d.ts +11 -0
  36. package/dist/stt/policy.js +11 -0
  37. package/dist/stt/protocol.d.ts +13 -0
  38. package/dist/stt/protocol.js +61 -0
  39. package/dist/stt/queue.d.ts +16 -0
  40. package/dist/stt/queue.js +45 -0
  41. package/dist/stt/stream.d.ts +48 -0
  42. package/dist/stt/stream.js +224 -0
  43. package/dist/stt/transport.d.ts +13 -0
  44. package/dist/stt/transport.js +45 -0
  45. package/dist/stt/types.d.ts +34 -0
  46. package/dist/stt/types.js +1 -0
  47. package/dist/{audio.js → tts/audio.js} +3 -2
  48. package/dist/tts/client.d.ts +39 -0
  49. package/dist/tts/client.js +121 -0
  50. package/dist/{policy.d.ts → tts/policy.d.ts} +2 -2
  51. package/dist/tts/policy.js +41 -0
  52. package/dist/{sentences.js → tts/sentences.js} +2 -2
  53. package/dist/{stream.d.ts → tts/stream.d.ts} +1 -1
  54. package/dist/{stream.js → tts/stream.js} +5 -8
  55. package/dist/{transport.d.ts → tts/transport.d.ts} +3 -11
  56. package/dist/tts/transport.js +116 -0
  57. package/dist/validation.d.ts +5 -0
  58. package/dist/validation.js +28 -0
  59. package/docs/realtime.md +220 -0
  60. package/docs/stt.md +103 -0
  61. package/docs/tts.md +139 -0
  62. package/package.json +14 -6
  63. package/dist/policy.js +0 -73
  64. /package/dist/{audio.d.ts → tts/audio.d.ts} +0 -0
  65. /package/dist/{queue.d.ts → tts/queue.d.ts} +0 -0
  66. /package/dist/{queue.js → tts/queue.js} +0 -0
  67. /package/dist/{sentences.d.ts → tts/sentences.d.ts} +0 -0
@@ -0,0 +1,224 @@
1
+ import { abortable, scope } from "../cancellation.js";
2
+ import { RimeError, RimeInputError, RimeCancelledError, RimeTimeoutError, RimeStreamError, } from "../errors.js";
3
+ import { InputAudio } from "./audio.js";
4
+ import { TranscriptQueue } from "./queue.js";
5
+ import { TranscriptState } from "./protocol.js";
6
+ import { TranscriptionCall } from "./transport.js";
7
+ import { policy } from "./policy.js";
8
+ /** Lazy, single-consumer stream. Source exhaustion commits input; return/cancel aborts it. */
9
+ export class TranscriptStream {
10
+ owner;
11
+ source;
12
+ options;
13
+ controller = new AbortController();
14
+ queue = new TranscriptQueue(policy.queuedUpdates);
15
+ timers = new Set();
16
+ worker = null;
17
+ workerFinished = false;
18
+ call = null;
19
+ failure = null;
20
+ reading = false;
21
+ finished = false;
22
+ inputDone = false;
23
+ accepted;
24
+ acceptance = new Promise((resolve) => {
25
+ this.accepted = resolve;
26
+ });
27
+ admission = null;
28
+ completion = null;
29
+ externalAbort = () => this.fail(new RimeCancelledError("Transcription cancelled"));
30
+ constructor(owner, source, options) {
31
+ this.owner = owner;
32
+ this.source = source;
33
+ this.options = options;
34
+ }
35
+ /** Server request identity, available once response headers arrive. */
36
+ get requestId() {
37
+ return this.call?.requestId ?? null;
38
+ }
39
+ deadline(seconds, message) {
40
+ const timer = scope([], seconds, new RimeTimeoutError(message));
41
+ timer.signal.addEventListener("abort", () => this.fail(timer.signal.reason), { once: true });
42
+ if (timer.signal.aborted)
43
+ this.fail(timer.signal.reason);
44
+ this.timers.add(timer);
45
+ return timer;
46
+ }
47
+ start() {
48
+ if (this.failure)
49
+ throw this.failure;
50
+ if (this.finished || this.worker)
51
+ return;
52
+ this.owner.checkOpen();
53
+ if (this.options.signal?.aborted) {
54
+ this.externalAbort();
55
+ throw this.failure;
56
+ }
57
+ this.options.signal?.addEventListener("abort", this.externalAbort, {
58
+ once: true,
59
+ });
60
+ if (this.options.timeout != null)
61
+ this.deadline(this.options.timeout, "Overall transcription deadline expired");
62
+ this.worker = this.run();
63
+ }
64
+ fail(error) {
65
+ if (this.failure || this.finished)
66
+ return;
67
+ if (error.requestId === null && this.requestId !== null)
68
+ error = new error.constructor(error.message, this.requestId, { cause: error.cause });
69
+ this.failure = error;
70
+ this.queue.fail(error);
71
+ for (const timer of this.timers)
72
+ timer.dispose();
73
+ this.controller.abort(error);
74
+ this.call?.cancel();
75
+ this.options.signal?.removeEventListener("abort", this.externalAbort);
76
+ if (!this.worker || this.workerFinished) {
77
+ this.source = null;
78
+ this.owner.forget(this);
79
+ }
80
+ }
81
+ async produce() {
82
+ const signal = this.controller.signal;
83
+ await abortable(this.acceptance, signal);
84
+ const audio = new InputAudio(this.options.inputFormat);
85
+ let iterator;
86
+ try {
87
+ try {
88
+ iterator = this.source[Symbol.asyncIterator]();
89
+ }
90
+ catch (error) {
91
+ throw new RimeInputError("The audio source failed", this.requestId, {
92
+ cause: error,
93
+ });
94
+ }
95
+ for (;;) {
96
+ let result;
97
+ try {
98
+ result = await abortable(iterator.next(), signal);
99
+ }
100
+ catch (error) {
101
+ if (signal.aborted)
102
+ throw signal.reason;
103
+ throw new RimeInputError("The audio source failed", this.requestId, {
104
+ cause: error,
105
+ });
106
+ }
107
+ if (result.done)
108
+ break;
109
+ for (const part of audio.feed(result.value))
110
+ await this.call.writeAudio(part);
111
+ }
112
+ audio.finish();
113
+ this.inputDone = true;
114
+ this.completion = this.deadline(policy.completionTimeout, "Transcription completion timed out");
115
+ this.call.finishInput();
116
+ }
117
+ finally {
118
+ if (iterator?.return) {
119
+ const cleanup = scope([], policy.cleanupTimeout, new RimeTimeoutError("Audio source cleanup timed out"));
120
+ try {
121
+ await abortable(Promise.resolve().then(() => iterator.return()), cleanup.signal);
122
+ }
123
+ catch {
124
+ /* Caller-owned source cleanup cannot replace the operation outcome. */
125
+ }
126
+ finally {
127
+ cleanup.dispose();
128
+ }
129
+ }
130
+ }
131
+ }
132
+ async read() {
133
+ const state = new TranscriptState(policy.transcriptBytes);
134
+ for await (const message of this.call.responses()) {
135
+ const update = state.accept(message, this.inputDone);
136
+ if (state.language !== null) {
137
+ this.admission?.dispose();
138
+ this.accepted();
139
+ }
140
+ if (update)
141
+ await this.queue.put(update);
142
+ }
143
+ return state.finish();
144
+ }
145
+ async run() {
146
+ let tasks = [];
147
+ try {
148
+ const prepared = await this.owner.prepare(this.controller.signal);
149
+ this.controller.signal.throwIfAborted();
150
+ this.call = new TranscriptionCall(prepared.client, prepared.metadata, this.controller.signal);
151
+ this.admission = this.deadline(policy.acceptanceTimeout, "Transcription acceptance timed out");
152
+ await this.call.start(this.options.language, this.options.mode ?? "written", this.options.contextTerms ?? []);
153
+ const producer = this.produce(), reader = this.read();
154
+ tasks = [producer, reader];
155
+ const [, final] = await Promise.all([producer, reader]);
156
+ this.completion?.dispose();
157
+ await this.queue.put(final);
158
+ this.queue.finish();
159
+ }
160
+ catch (error) {
161
+ this.fail(error instanceof RimeError
162
+ ? error
163
+ : new RimeStreamError("Transcription failed", this.requestId, {
164
+ cause: error,
165
+ }));
166
+ }
167
+ finally {
168
+ if (this.failure)
169
+ this.controller.abort(this.failure);
170
+ this.call?.cancel();
171
+ await Promise.allSettled(tasks);
172
+ this.source = null;
173
+ this.workerFinished = true;
174
+ if (this.failure)
175
+ this.owner.forget(this);
176
+ }
177
+ }
178
+ [Symbol.asyncIterator]() {
179
+ return this;
180
+ }
181
+ async next() {
182
+ if (this.reading)
183
+ throw new RimeInputError("TranscriptStream permits only one concurrent reader");
184
+ this.reading = true;
185
+ try {
186
+ this.start();
187
+ if (this.finished)
188
+ return { done: true, value: undefined };
189
+ const result = await this.queue.get();
190
+ if (this.failure)
191
+ throw this.failure;
192
+ if (!result.done && result.value.kind === "final") {
193
+ await this.cleanup();
194
+ if (this.failure)
195
+ throw this.failure;
196
+ this.finished = true;
197
+ }
198
+ return result;
199
+ }
200
+ finally {
201
+ this.reading = false;
202
+ }
203
+ }
204
+ async cleanup() {
205
+ await this.worker;
206
+ for (const timer of this.timers)
207
+ timer.dispose();
208
+ this.options.signal?.removeEventListener("abort", this.externalAbort);
209
+ this.owner.forget(this);
210
+ }
211
+ /** Cancel unfinished work and release owned resources; safe to repeat. */
212
+ async cancel() {
213
+ this.fail(new RimeCancelledError("Transcription cancelled"));
214
+ await this.cleanup();
215
+ this.source = null;
216
+ }
217
+ async return() {
218
+ await this.cancel();
219
+ return { done: true, value: undefined };
220
+ }
221
+ async [Symbol.asyncDispose]() {
222
+ await this.cancel();
223
+ }
224
+ }
@@ -0,0 +1,13 @@
1
+ import * as grpc from "@grpc/grpc-js";
2
+ import * as schema from "@rimelabs/api";
3
+ import { BidiCall } from "../grpc.js";
4
+ import type { TranscriptionMode } from "./types.js";
5
+ export declare function requireSchema(): void;
6
+ export declare const transport: {
7
+ makeClient: (target: string) => grpc.Client;
8
+ };
9
+ export declare class TranscriptionCall extends BidiCall<schema.StreamingTranscriptionRequest, schema.StreamingTranscriptionResponse> {
10
+ constructor(client: grpc.Client, auth: grpc.Metadata, signal: AbortSignal);
11
+ start(language: string, mode: TranscriptionMode, contextTerms: readonly string[]): Promise<void>;
12
+ writeAudio(audio: Uint8Array): Promise<void>;
13
+ }
@@ -0,0 +1,45 @@
1
+ import * as grpc from "@grpc/grpc-js";
2
+ import { create } from "@bufbuild/protobuf";
3
+ import * as schema from "@rimelabs/api";
4
+ import { BidiCall, serializer, deserializer } from "../grpc.js";
5
+ import { RimeInputError } from "../errors.js";
6
+ import { policy } from "./policy.js";
7
+ export function requireSchema() {
8
+ if (!schema.SpeechToText)
9
+ throw new RimeInputError("STT requires @rimelabs/api with SpeechToText definitions");
10
+ }
11
+ export const transport = {
12
+ makeClient: (target) => new grpc.Client(target, grpc.credentials.createSsl(), {
13
+ "grpc.enable_retries": 0,
14
+ "grpc.max_receive_message_length": policy.receiveBytes,
15
+ "grpc.max_send_message_length": 131072,
16
+ }),
17
+ };
18
+ export class TranscriptionCall extends BidiCall {
19
+ constructor(client, auth, signal) {
20
+ requireSchema();
21
+ const service = schema.SpeechToText;
22
+ const method = service.method.transcribeStreaming;
23
+ super(client.makeBidiStreamRequest(`/${service.typeName}/${method.name}`, serializer(method.input), deserializer(method.output), auth), signal);
24
+ }
25
+ start(language, mode, contextTerms) {
26
+ return this.write(create(schema.StreamingTranscriptionRequestSchema, {
27
+ payload: {
28
+ case: "config",
29
+ value: {
30
+ language,
31
+ mode: mode === "verbatim"
32
+ ? schema.TranscriptionMode.VERBATIM
33
+ : schema.TranscriptionMode.WRITTEN,
34
+ contextTerms: [...contextTerms],
35
+ outputContract: schema.StreamingOutputContract.REVISED_HYPOTHESES,
36
+ },
37
+ },
38
+ }));
39
+ }
40
+ writeAudio(audio) {
41
+ return this.write(create(schema.StreamingTranscriptionRequestSchema, {
42
+ payload: { case: "audio", value: audio },
43
+ }));
44
+ }
45
+ }
@@ -0,0 +1,34 @@
1
+ import type { PCMFormat } from "../pcm.js";
2
+ /** Formatting intent, for example "flight 247" or "flight two four seven". */
3
+ export type TranscriptionMode = "written" | "verbatim";
4
+ /** One caller-ended utterance of headerless signed PCM16 little-endian audio. */
5
+ export type AudioSource = AsyncIterable<Uint8Array>;
6
+ export interface TranscriptionOptions {
7
+ /** Required BCP-47 tag, passed unchanged to the service (for example "en"). */
8
+ language: string;
9
+ /** Formatting intent; defaults to written and does not guarantee exact tokens. */
10
+ mode?: TranscriptionMode;
11
+ /** Recognition hints passed unchanged and in order, for example ["Super-G"]. */
12
+ contextTerms?: readonly string[];
13
+ /** Defaults to PCM16 little-endian mono 16 kHz; fixed for the utterance. */
14
+ inputFormat?: PCMFormat;
15
+ /** Optional overall deadline in seconds, including source and consumer pauses. */
16
+ timeout?: number | null;
17
+ /** Cancels only this utterance, including a pending connection. */
18
+ signal?: AbortSignal;
19
+ }
20
+ export interface TranscriptionPartial {
21
+ /** A revisable replacement snapshot. */
22
+ readonly kind: "partial";
23
+ /** Complete current text; replace previous text instead of appending it. */
24
+ readonly text: string;
25
+ }
26
+ export interface TranscriptionFinal {
27
+ /** Successful protocol and transport completion. */
28
+ readonly kind: "final";
29
+ /** Complete final text; silence can produce an empty string. */
30
+ readonly text: string;
31
+ /** Canonical BCP-47 tag selected by the service, for example "en". */
32
+ readonly language: string;
33
+ }
34
+ export type TranscriptionUpdate = TranscriptionPartial | TranscriptionFinal;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,4 @@
1
- import { RimeAudioFormatError } from "./errors.js";
1
+ import { RimeAudioFormatError } from "../errors.js";
2
2
  export class AudioFormat {
3
3
  encoding;
4
4
  sampleRate;
@@ -20,7 +20,8 @@ const total = raw.reduce((a, b) => a + b, 0);
20
20
  const coefficients = raw.map((x) => x / total);
21
21
  function mulaw(sample) {
22
22
  sample = Math.max(-32768, Math.min(32767, sample));
23
- const sign = sample < 0 ? 128 : 0, magnitude = Math.min(Math.abs(sample), 32635) + 132;
23
+ // G.711 uses one's complement for negative PCM before quantization.
24
+ const sign = sample < 0 ? 128 : 0, magnitude = Math.min(sample < 0 ? ~sample : sample, 32635) + 132;
24
25
  const exponent = Math.max(0, Math.floor(Math.log2(magnitude)) - 7);
25
26
  return ~(sign | (exponent << 4) | ((magnitude >> (exponent + 3)) & 15)) & 255;
26
27
  }
@@ -0,0 +1,39 @@
1
+ import { AudioFormat } from "./audio.js";
2
+ import { AudioStream, type TextSource } from "./stream.js";
3
+ import { Credentials } from "../auth.js";
4
+ import { policy } from "./policy.js";
5
+ export interface SynthesisOptions {
6
+ voice?: string | null;
7
+ language?: string;
8
+ audioFormat?: AudioFormat | null;
9
+ timeout?: number | null;
10
+ }
11
+ export interface DiscoveryOptions {
12
+ timeout?: number | null;
13
+ }
14
+ export interface VoiceListOptions extends DiscoveryOptions {
15
+ language?: string | null;
16
+ }
17
+ export declare class TTS {
18
+ private readonly credentials;
19
+ private readonly deployment;
20
+ private readonly defaultTimeout;
21
+ private readonly checkOpen;
22
+ private client;
23
+ private streams;
24
+ private discoveryOperations;
25
+ readonly voices: {
26
+ list: (options?: VoiceListOptions) => Promise<string[]>;
27
+ };
28
+ readonly languages: {
29
+ list: (options?: DiscoveryOptions) => Promise<string[]>;
30
+ };
31
+ constructor(credentials: Credentials, deployment: Readonly<typeof policy>, defaultTimeout: number | null, checkOpen: () => void);
32
+ private prepare;
33
+ /** Synthesize complete text and return an audio stream. Do not await this call. */
34
+ synthesize(text: string, options?: SynthesisOptions): AudioStream;
35
+ /** Stream text chunks into synthesis and return an audio stream. Do not await this call. */
36
+ stream(text: TextSource, options?: SynthesisOptions): AudioStream;
37
+ private discover;
38
+ close(): Promise<void>;
39
+ }
@@ -0,0 +1,121 @@
1
+ import { AudioFormat } from "./audio.js";
2
+ import { AudioStream, constructionKey } from "./stream.js";
3
+ import { policy, timeout, nonempty } from "./policy.js";
4
+ import { abortable } from "../cancellation.js";
5
+ import { transport, connect, metadata, discover, } from "./transport.js";
6
+ import { RimeInputError, RimeAudioFormatError, RimeCancelledError, RimeTimeoutError, RimeUnavailableError, } from "../errors.js";
7
+ export class TTS {
8
+ credentials;
9
+ deployment;
10
+ defaultTimeout;
11
+ checkOpen;
12
+ client = null;
13
+ streams = new Set();
14
+ discoveryOperations = new Map();
15
+ voices;
16
+ languages;
17
+ constructor(credentials, deployment, defaultTimeout, checkOpen) {
18
+ this.credentials = credentials;
19
+ this.deployment = deployment;
20
+ this.defaultTimeout = defaultTimeout;
21
+ this.checkOpen = checkOpen;
22
+ this.voices = {
23
+ list: async (options = {}) => {
24
+ if (options.language !== undefined && options.language !== null)
25
+ nonempty(options.language, "language");
26
+ return this.discover("voices", options.language, options.timeout);
27
+ },
28
+ };
29
+ this.languages = {
30
+ list: (options = {}) => this.discover("languages", undefined, options.timeout),
31
+ };
32
+ }
33
+ async prepare(signal) {
34
+ this.checkOpen();
35
+ const auth = await this.credentials.metadata(signal);
36
+ this.checkOpen();
37
+ this.client ??= transport.makeClient(this.deployment.target);
38
+ await connect(this.client, signal);
39
+ return { client: this.client, metadata: metadata(auth) };
40
+ }
41
+ /** Synthesize complete text and return an audio stream. Do not await this call. */
42
+ synthesize(text, options = {}) {
43
+ this.checkOpen();
44
+ if (typeof text !== "string")
45
+ throw new RimeInputError("text must be a string; use stream() for an async text source");
46
+ if (!text.trim())
47
+ throw new RimeInputError("Text must contain non-whitespace characters");
48
+ return this.stream((async function* () {
49
+ yield text;
50
+ })(), options);
51
+ }
52
+ /** Stream text chunks into synthesis and return an audio stream. Do not await this call. */
53
+ stream(text, options = {}) {
54
+ this.checkOpen();
55
+ if (typeof text === "string" ||
56
+ !text ||
57
+ typeof text[Symbol.asyncIterator] !== "function")
58
+ throw new RimeInputError("text must be an async iterable of strings; use synthesize() for a string");
59
+ const voice = options.voice === undefined || options.voice === null
60
+ ? this.deployment.defaultVoice
61
+ : nonempty(options.voice, "voice");
62
+ const language = nonempty(options.language ?? "en", "language");
63
+ const format = options.audioFormat ?? AudioFormat.PCM_24000;
64
+ if (format !== AudioFormat.PCM_24000 && format !== AudioFormat.MULAW_8000)
65
+ throw new RimeAudioFormatError("Select a named AudioFormat profile");
66
+ const audio = new AudioStream(constructionKey, {
67
+ prepare: (s) => this.prepare(s),
68
+ checkOpen: () => this.checkOpen(),
69
+ forget: (s) => this.streams.delete(s),
70
+ }, text, voice, language, format, timeout(options.timeout, this.defaultTimeout));
71
+ this.streams.add(audio);
72
+ return audio;
73
+ }
74
+ async discover(kind, language, override) {
75
+ this.checkOpen();
76
+ const budget = Math.min(timeout(override, this.defaultTimeout) ?? Infinity, policy.discoveryTimeout);
77
+ const controller = new AbortController();
78
+ let requestId = null;
79
+ const timer = setTimeout(() => controller.abort(new RimeTimeoutError("Discovery deadline expired")), budget * 1000);
80
+ const operation = (async () => {
81
+ try {
82
+ const prepared = await this.prepare(controller.signal);
83
+ for (let attempt = 0;; attempt++) {
84
+ requestId = null;
85
+ try {
86
+ return await discover(prepared, kind, language, controller.signal);
87
+ }
88
+ catch (error) {
89
+ if (!(error instanceof RimeUnavailableError) || attempt === 2)
90
+ throw error;
91
+ requestId = error.requestId;
92
+ await abortable(new Promise((resolve) => setTimeout(resolve, 50 * 2 ** attempt)), controller.signal);
93
+ }
94
+ }
95
+ }
96
+ catch (error) {
97
+ if (error instanceof RimeTimeoutError && requestId !== null)
98
+ throw new RimeTimeoutError(error.message, requestId, {
99
+ cause: error.cause,
100
+ });
101
+ throw error;
102
+ }
103
+ finally {
104
+ clearTimeout(timer);
105
+ this.discoveryOperations.delete(controller);
106
+ }
107
+ })();
108
+ this.discoveryOperations.set(controller, operation);
109
+ return operation;
110
+ }
111
+ async close() {
112
+ const discoveries = [...this.discoveryOperations.values()];
113
+ for (const controller of this.discoveryOperations.keys())
114
+ controller.abort(new RimeCancelledError("Client closed during discovery"));
115
+ await Promise.all([
116
+ ...[...this.streams].map((stream) => stream.cancel()),
117
+ Promise.allSettled(discoveries),
118
+ ]);
119
+ this.client?.close();
120
+ }
121
+ }
@@ -1,6 +1,8 @@
1
+ export { timeout } from "../validation.js";
1
2
  export declare const policy: {
2
3
  target: string;
3
4
  audience: string;
5
+ defaultVoice: string;
4
6
  exchangeUrl: string;
5
7
  sentenceBytes: number;
6
8
  sourceChars: number;
@@ -15,6 +17,4 @@ export declare const policy: {
15
17
  cleanupTimeout: number;
16
18
  };
17
19
  export declare function resolve(model: string, endpoint?: string | null): Readonly<typeof policy>;
18
- export declare function timeout(value: unknown, inherited?: number | null): number | null;
19
20
  export declare function nonempty(value: unknown, name: string): string;
20
- export declare function abortable<T>(promise: PromiseLike<T>, signal: AbortSignal): Promise<T>;
@@ -0,0 +1,41 @@
1
+ import { endpointAddress } from "../validation.js";
2
+ export { timeout } from "../validation.js";
3
+ import { RimeInputError } from "../errors.js";
4
+ const codaHostname = "coda.api.rime.ai";
5
+ export const policy = {
6
+ target: `${codaHostname}:443`,
7
+ audience: codaHostname,
8
+ defaultVoice: "clementine",
9
+ exchangeUrl: "https://themis.api.rime.ai/v1/token",
10
+ sentenceBytes: 65536,
11
+ sourceChars: 1024,
12
+ outputBytes: 96000,
13
+ outputChunkBytes: 9600,
14
+ receiveBytes: 4194304,
15
+ authTimeout: 10,
16
+ connectionTimeout: 10,
17
+ firstAudioTimeout: 30,
18
+ progressTimeout: 60,
19
+ discoveryTimeout: 10,
20
+ cleanupTimeout: 2,
21
+ };
22
+ export function resolve(model, endpoint) {
23
+ const deployment = { ...policy };
24
+ if (model === "mistv3") {
25
+ const hostname = "mist.api.rime.ai";
26
+ deployment.target = `${hostname}:443`;
27
+ deployment.audience = hostname;
28
+ deployment.defaultVoice = "astra";
29
+ }
30
+ else if (model !== "coda") {
31
+ throw new RimeInputError("model must be 'coda' or 'mistv3'");
32
+ }
33
+ if (endpoint === undefined || endpoint === null)
34
+ return deployment;
35
+ return { ...deployment, ...endpointAddress(endpoint) };
36
+ }
37
+ export function nonempty(value, name) {
38
+ if (typeof value !== "string" || !value.trim())
39
+ throw new RimeInputError(`${name} must be a non-empty string`);
40
+ return value;
41
+ }
@@ -1,7 +1,7 @@
1
1
  import { createRequire } from "node:module";
2
- import { RimeInputError, RimeResourceLimitError } from "./errors.js";
2
+ import { RimeInputError, RimeResourceLimitError } from "../errors.js";
3
3
  const require = createRequire(import.meta.url);
4
- const module = require("../vendor/blingfire.cjs");
4
+ const module = require("../../vendor/blingfire.cjs");
5
5
  export const ready = module.ready;
6
6
  // These marks schedule scans. Only BlingFire decides sentence boundaries.
7
7
  // Keep the set and code-point counts in sync with Python's _sentences.py.
@@ -5,7 +5,7 @@ export interface StreamOwner {
5
5
  checkOpen(): void;
6
6
  forget(stream: AudioStream): void;
7
7
  }
8
- export type TextSource = string | AsyncIterable<string>;
8
+ export type TextSource = AsyncIterable<string>;
9
9
  export declare const constructionKey: unique symbol;
10
10
  export declare class AudioStream implements AsyncIterableIterator<Uint8Array> {
11
11
  private owner;
@@ -2,11 +2,12 @@ import { randomUUID } from "node:crypto";
2
2
  import { debuglog } from "node:util";
3
3
  const log = debuglog("rime-sdk");
4
4
  import { Converter } from "./audio.js";
5
- import { policy, abortable } from "./policy.js";
5
+ import { policy } from "./policy.js";
6
+ import { abortable } from "../cancellation.js";
6
7
  import { ByteQueue } from "./queue.js";
7
8
  import { SentenceBuffer, ready } from "./sentences.js";
8
9
  import { SynthesisCall } from "./transport.js";
9
- import { RimeError, RimeInputError, RimeCancelledError, RimeTimeoutError, RimeStreamError, } from "./errors.js";
10
+ import { RimeError, RimeInputError, RimeCancelledError, RimeTimeoutError, RimeStreamError, } from "../errors.js";
10
11
  export const constructionKey = Symbol("private stream constructor");
11
12
  export class AudioStream {
12
13
  owner;
@@ -40,7 +41,7 @@ export class AudioStream {
40
41
  this.formatValue = formatValue;
41
42
  this.timeout = timeout;
42
43
  if (key !== constructionKey)
43
- throw new TypeError("AudioStream is returned by client.tts.stream()");
44
+ throw new TypeError("AudioStream is returned by client.tts.synthesize() or client.tts.stream()");
44
45
  }
45
46
  get format() {
46
47
  return this.formatValue;
@@ -99,11 +100,7 @@ export class AudioStream {
99
100
  let meaningful = false;
100
101
  try {
101
102
  try {
102
- iterator = (typeof source === "string"
103
- ? (async function* () {
104
- yield source;
105
- })()
106
- : source)[Symbol.asyncIterator]();
103
+ iterator = source[Symbol.asyncIterator]();
107
104
  }
108
105
  catch (error) {
109
106
  if (this.controller.signal.aborted)
@@ -1,31 +1,23 @@
1
+ export { metadata } from "../grpc.js";
2
+ export { rpcError } from "../grpc.js";
1
3
  import * as grpc from "@grpc/grpc-js";
2
- import * as errors from "./errors.js";
3
4
  export type Connection = grpc.Client;
4
5
  export interface PreparedConnection {
5
6
  client: Connection;
6
7
  metadata: grpc.Metadata;
7
8
  }
8
9
  export type DiscoveryKind = "voices" | "languages";
9
- export declare function rpcError(code: number, requestId?: string | null): errors.RimeError;
10
10
  export declare const transport: {
11
11
  makeClient: (target: string) => grpc.Client;
12
12
  };
13
13
  export declare function connect(client: grpc.Client, signal: AbortSignal): Promise<void>;
14
- export declare function metadata(value: string): grpc.Metadata;
15
14
  export declare function discover(prepared: PreparedConnection, kind: DiscoveryKind, language: string | null | undefined, signal: AbortSignal): Promise<string[]>;
16
15
  export declare class SynthesisCall {
17
- private signal;
18
- private readonly call;
19
- private readonly metadataPromise;
20
- private readonly statusPromise;
21
- private status;
22
- private id;
16
+ private readonly rpc;
23
17
  constructor(prepared: PreparedConnection, signal: AbortSignal);
24
18
  get requestId(): string | null;
25
19
  get done(): boolean;
26
20
  cancel(): void;
27
- private error;
28
- private writeMessage;
29
21
  start(voice: string, language: string): Promise<void>;
30
22
  write(sentence: string): Promise<void>;
31
23
  finishInput(): void;