react-native-nobodywho 2.5.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nobodywho",
3
- "version": "2.5.0",
3
+ "version": "3.0.0",
4
4
  "description": "Run LLMs locally with offline inference — React Native bindings",
5
5
  "main": "src/wrapper.ts",
6
6
  "types": "src/wrapper.ts",
package/src/encoder.ts CHANGED
@@ -46,6 +46,11 @@ export class Encoder {
46
46
  return this._inner.encode(text);
47
47
  }
48
48
 
49
+ /** Encode multiple texts, preserving input order. */
50
+ async encodeBatch(texts: string[]): Promise<number[][]> {
51
+ return this._inner.encodeBatch(texts);
52
+ }
53
+
49
54
  /**
50
55
  * Immediately free the underlying Rust resources.
51
56
  * After calling this, the Encoder instance is no longer usable.
@@ -1,8 +1,8 @@
1
- import type { RustSttInterface } from "../generated/ts/nobodywho";
1
+ import type { RustSpeechToTextInterface } from "../generated/ts/nobodywho";
2
2
  import * as nobodywho from "../generated/ts/nobodywho";
3
3
  import { TokenStream } from "./streaming";
4
4
 
5
- export type SttOptions = {
5
+ export type SpeechToTextOptions = {
6
6
  source: string;
7
7
  language?: string;
8
8
  quantization?: string;
@@ -13,7 +13,7 @@ export type SttOptions = {
13
13
  *
14
14
  * @example
15
15
  * ```typescript
16
- * const stt = new STT({
16
+ * const stt = await SpeechToText.load({
17
17
  * source: "hf://onnx-community/whisper-base",
18
18
  * language: "en",
19
19
  * });
@@ -26,24 +26,32 @@ export type SttOptions = {
26
26
  * const text = await stream.completed();
27
27
  * ```
28
28
  */
29
- export class STT {
29
+ export class SpeechToText {
30
30
  /** @internal */
31
- private readonly _inner: RustSttInterface;
31
+ private readonly _inner: RustSpeechToTextInterface;
32
+
33
+ /** @internal */
34
+ private constructor(inner: RustSpeechToTextInterface) {
35
+ this._inner = inner;
36
+ }
32
37
 
33
38
  /**
34
- * @param opts - See {@link SttOptions}.
39
+ * Load a Whisper SpeechToText model.
40
+ *
41
+ * @param opts - See {@link SpeechToTextOptions}.
35
42
  */
36
- constructor(opts: SttOptions) {
37
- this._inner = new nobodywho.RustStt(
43
+ static async load(opts: SpeechToTextOptions): Promise<SpeechToText> {
44
+ const inner = await nobodywho.loadSpeechToText(
38
45
  opts.source,
39
46
  opts.language,
40
47
  opts.quantization,
41
48
  );
49
+ return new SpeechToText(inner);
42
50
  }
43
51
 
44
52
  /**
45
53
  * Transcribe an audio file (WAV / MP3).
46
- * Returns an `STTStream` to consume tokens as they arrive.
54
+ * Returns an `SpeechToTextStream` to consume tokens as they arrive.
47
55
  */
48
56
  transcribeFile(path: string): TokenStream {
49
57
  return new TokenStream(this._inner.transcribeFile(path));
@@ -1,15 +1,15 @@
1
1
  import {
2
- RustTts,
3
- loadTts,
4
- type RustTtsInterface,
2
+ RustTextToSpeech,
3
+ loadTextToSpeech,
4
+ type RustTextToSpeechInterface,
5
5
  } from "../generated/ts/nobodywho";
6
6
 
7
- export type TtsArchitecture = "kokoro" | "pocket-tts" | "supertonic";
8
- export type TtsDevice = "auto" | "cpu" | "cuda";
7
+ export type TextToSpeechArchitecture = "kokoro" | "pocket-tts" | "supertonic";
8
+ export type TextToSpeechDevice = "auto" | "cpu" | "cuda";
9
9
 
10
- export type TtsOptions = {
10
+ export type TextToSpeechOptions = {
11
11
  source: string;
12
- architecture?: TtsArchitecture;
12
+ architecture?: TextToSpeechArchitecture;
13
13
  voice?: string;
14
14
  language?: string;
15
15
  speed?: number;
@@ -18,17 +18,17 @@ export type TtsOptions = {
18
18
  precision?: "int8" | "fp32";
19
19
  temperature?: number;
20
20
  huggingfaceToken?: string;
21
- device?: TtsDevice;
21
+ device?: TextToSpeechDevice;
22
22
  };
23
23
 
24
24
  /** Text-to-speech synthesizer that returns WAV bytes. */
25
- export class Tts {
25
+ export class TextToSpeech {
26
26
  /** @internal */
27
- private _inner: RustTtsInterface;
27
+ private _inner: RustTextToSpeechInterface;
28
28
 
29
- /** Create a TTS synthesizer synchronously. */
30
- constructor(opts: TtsOptions) {
31
- this._inner = new RustTts(
29
+ /** Create a TextToSpeech synthesizer synchronously. */
30
+ constructor(opts: TextToSpeechOptions) {
31
+ this._inner = new RustTextToSpeech(
32
32
  opts.source,
33
33
  opts.architecture,
34
34
  opts.voice,
@@ -43,15 +43,15 @@ export class Tts {
43
43
  );
44
44
  }
45
45
 
46
- private static fromInner(inner: RustTtsInterface): Tts {
47
- const tts = Object.create(Tts.prototype) as Tts;
46
+ private static fromInner(inner: RustTextToSpeechInterface): TextToSpeech {
47
+ const tts = Object.create(TextToSpeech.prototype) as TextToSpeech;
48
48
  tts._inner = inner;
49
49
  return tts;
50
50
  }
51
51
 
52
- /** Create a TTS synthesizer asynchronously. */
53
- static async load(opts: TtsOptions): Promise<Tts> {
54
- const inner = await loadTts(
52
+ /** Create a TextToSpeech synthesizer asynchronously. */
53
+ static async load(opts: TextToSpeechOptions): Promise<TextToSpeech> {
54
+ const inner = await loadTextToSpeech(
55
55
  opts.source,
56
56
  opts.architecture,
57
57
  opts.voice,
@@ -64,7 +64,7 @@ export class Tts {
64
64
  opts.huggingfaceToken,
65
65
  opts.device ?? "auto",
66
66
  );
67
- return Tts.fromInner(inner);
67
+ return TextToSpeech.fromInner(inner);
68
68
  }
69
69
 
70
70
  /** Synthesize text and return WAV bytes. */
@@ -79,7 +79,7 @@ export class Tts {
79
79
 
80
80
  /**
81
81
  * Immediately free the underlying Rust resources.
82
- * After calling this, the Tts instance is no longer usable.
82
+ * After calling this, the TextToSpeech instance is no longer usable.
83
83
  */
84
84
  destroy(): void {
85
85
  (this._inner as any).uniffiDestroy();
package/src/vad.ts ADDED
@@ -0,0 +1,104 @@
1
+ import type { RustVoiceActivityDetectionInterface, VoiceActivityDetectionEvent } from "../generated/ts/nobodywho";
2
+ import * as nobodywho from "../generated/ts/nobodywho";
3
+
4
+ export { VoiceActivityDetectionEvent } from "../generated/ts/nobodywho";
5
+
6
+ export type VoiceActivityDetectionOptions = {
7
+ source?: string;
8
+ sampleRate: number;
9
+ threshold?: number;
10
+ minSilenceDurationMs?: number;
11
+ minSpeechDurationMs?: number;
12
+ prerollDurationMs?: number;
13
+ };
14
+
15
+ /**
16
+ * Voice activity detection from live, streaming audio, backed by Silero VAD.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const vad = await VoiceActivityDetection.load({ sampleRate: 16000 });
21
+ *
22
+ * // Feed each newest chunk as it arrives (not the whole buffer — VoiceActivityDetection
23
+ * // tracks the current turn internally).
24
+ * if (vad.push(chunk) === VoiceActivityDetectionEvent.SpeechEnded) {
25
+ * const audio = vad.finish();
26
+ * // audio: Int16Array-like number[] spanning SpeechStarted (with a
27
+ * // small pre-roll) through SpeechEnded.
28
+ * }
29
+ * ```
30
+ */
31
+ export class VoiceActivityDetection {
32
+ /** @internal */
33
+ private readonly _inner: RustVoiceActivityDetectionInterface;
34
+
35
+ /** @internal */
36
+ private constructor(inner: RustVoiceActivityDetectionInterface) {
37
+ this._inner = inner;
38
+ }
39
+
40
+ /**
41
+ * Load a voice activity detector.
42
+ *
43
+ * @param opts - See {@link VoiceActivityDetectionOptions}.
44
+ */
45
+ static async load(opts: VoiceActivityDetectionOptions): Promise<VoiceActivityDetection> {
46
+ const inner = await nobodywho.loadVoiceActivityDetection(
47
+ opts.source,
48
+ opts.sampleRate,
49
+ opts.threshold,
50
+ opts.minSilenceDurationMs,
51
+ opts.minSpeechDurationMs,
52
+ opts.prerollDurationMs,
53
+ undefined,
54
+ );
55
+ return new VoiceActivityDetection(inner);
56
+ }
57
+
58
+ /**
59
+ * Feed the newest chunk of audio (not the whole accumulated buffer —
60
+ * `VoiceActivityDetection` tracks the current turn internally). Always
61
+ * returns the current confirmed state: `Speech`/`Silence` if unchanged
62
+ * since the last call, or `SpeechStarted`/`SpeechEnded` on the call that
63
+ * confirmed the transition.
64
+ *
65
+ * @param chunk - Flat array of signed 16-bit samples (mono).
66
+ */
67
+ push(chunk: Int16Array | number[]): VoiceActivityDetectionEvent {
68
+ const arr = chunk instanceof Int16Array ? Array.from(chunk) : chunk;
69
+ return this._inner.push(arr);
70
+ }
71
+
72
+ /**
73
+ * Return the current turn's captured audio (from the confirmed
74
+ * `SpeechStarted`, including a small pre-roll, through to `SpeechEnded`)
75
+ * and reset internal state for the next turn. Empty if speech was never
76
+ * confirmed.
77
+ */
78
+ finish(): number[] {
79
+ return this._inner.finish();
80
+ }
81
+
82
+ /**
83
+ * Detect every speech segment in a complete audio buffer, returning
84
+ * each segment's audio (with a short pre-roll) in order. Unlike `push`,
85
+ * correctly finds every segment regardless of buffer size — use this
86
+ * for offline/batch processing instead of live streaming.
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * for (const audio of vad.segment(fullRecording)) {
91
+ * transcribe(audio);
92
+ * }
93
+ * ```
94
+ */
95
+ segment(samples: Int16Array | number[]): number[][] {
96
+ const arr = samples instanceof Int16Array ? Array.from(samples) : samples;
97
+ return this._inner.segment(arr);
98
+ }
99
+
100
+ /** Release native resources. Call when done with this handle. */
101
+ destroy(): void {
102
+ (this._inner as any).uniffiDestroy?.();
103
+ }
104
+ }
package/src/wrapper.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * NobodyWho — React Native bindings for local LLM inference.
3
3
  *
4
4
  * This is the public entry point. It re-exports the public API:
5
- * - Wrapper classes: Model, Chat, Encoder, CrossEncoder, Tts, Tool, TokenStream, Prompt
5
+ * - Wrapper classes: Model, Chat, Encoder, CrossEncoder, TextToSpeech, Tool, TokenStream, Prompt
6
6
  * - Direct re-exports: SamplerBuilder, SamplerConfig
7
7
  * - Types: Message, Asset, ToolCall
8
8
  * - Utilities: SamplerPresets, cosineSimilarity, downloadModel
@@ -57,10 +57,11 @@ export async function downloadModel(opts: {
57
57
  // Wrapper classes
58
58
  export { Model } from "./model";
59
59
  export { Chat } from "./chat";
60
- export { STT } from "./stt";
60
+ export { SpeechToText } from "./speech_to_text";
61
+ export { VoiceActivityDetection } from "./vad";
61
62
  export { Encoder } from "./encoder";
62
63
  export { CrossEncoder } from "./cross_encoder";
63
- export { Tts } from "./tts";
64
+ export { TextToSpeech } from "./text_to_speech";
64
65
  export { Prompt } from "./prompt";
65
66
  export { Tool } from "./tool";
66
67
  export { TokenStream } from "./streaming";
@@ -85,8 +86,10 @@ export type {
85
86
  CachedModel,
86
87
  ChatStats,
87
88
  } from "../generated/ts/nobodywho";
88
- export type { TtsArchitecture, TtsDevice, TtsOptions } from "./tts";
89
- export type { SttOptions } from "./stt";
89
+ export type { TextToSpeechArchitecture, TextToSpeechDevice, TextToSpeechOptions } from "./text_to_speech";
90
+ export type { SpeechToTextOptions } from "./speech_to_text";
91
+ export { VoiceActivityDetectionEvent } from "./vad";
92
+ export type { VoiceActivityDetectionOptions } from "./vad";
90
93
 
91
94
  // Ergonomic wrapper additions.
92
95
  export { SamplerPresets } from "./sampler_presets";