@dtelecom/stt 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 dTelecom
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # @dtelecom/stt
2
+
3
+ TypeScript SDK for dTelecom real-time speech-to-text with [x402](https://www.x402.org/) micropayments.
4
+
5
+ Pay-per-minute STT powered by Whisper and Parakeet, with automatic blockchain payments on Base.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @dtelecom/stt
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { STTClient } from "@dtelecom/stt";
17
+
18
+ const client = new STTClient({ privateKey: "0x..." });
19
+
20
+ const stream = await client.session({ minutes: 5, language: "en" }).open();
21
+ try {
22
+ for await (const t of stream.transcribeFile("meeting.wav")) {
23
+ console.log(`[${t.start?.toFixed(1)}s] ${t.text}`);
24
+ }
25
+ } finally {
26
+ await stream.close();
27
+ }
28
+ ```
29
+
30
+ ## Real-Time Streaming
31
+
32
+ ```typescript
33
+ import { STTClient } from "@dtelecom/stt";
34
+
35
+ const client = new STTClient({ privateKey: "0x..." });
36
+
37
+ const stream = await client.session({ minutes: 5, language: "en" }).open();
38
+
39
+ // Callback-based
40
+ stream.onTranscription((t) => console.log(t.text));
41
+
42
+ // Send audio chunks (PCM16, 16kHz, mono)
43
+ await stream.sendAudio(pcmBuffer);
44
+
45
+ // Or iterate asynchronously
46
+ for await (const t of stream.transcriptions()) {
47
+ console.log(t.text);
48
+ }
49
+
50
+ await stream.close();
51
+ ```
52
+
53
+ ## Auto-Extend Sessions
54
+
55
+ Sessions automatically buy more time when running low (enabled by default):
56
+
57
+ ```typescript
58
+ // 30-minute session that auto-extends
59
+ const stream = await client.session({ minutes: 30, language: "en" }).open();
60
+ // When <60s remaining, SDK buys 5 more minutes automatically
61
+
62
+ // Disable auto-extend
63
+ const stream = await client.session({ minutes: 5, autoExtend: false }).open();
64
+ ```
65
+
66
+ ## Audio Format
67
+
68
+ The server expects **PCM16, 16kHz, mono** audio. Convert with ffmpeg:
69
+
70
+ ```bash
71
+ ffmpeg -i input.mp3 -ar 16000 -ac 1 -acodec pcm_s16le output.wav
72
+ ```
73
+
74
+ ## Pricing
75
+
76
+ ```typescript
77
+ const info = await client.pricing();
78
+ console.log(`$${info.pricePerMinuteUsd}/min (${info.currency} on ${info.network})`);
79
+ ```
80
+
81
+ Current pricing: **$0.005/min** (USDC on Base).
82
+
83
+ ## API Reference
84
+
85
+ ### `new STTClient({ privateKey, url? })`
86
+
87
+ Main client. Default URL: `https://x402stt.dtelecom.org`.
88
+
89
+ - `session({ minutes?, language?, autoExtend? })` — Create a session context
90
+ - `pricing()` — Get pricing info
91
+ - `health()` — Check server health
92
+
93
+ ### `SessionContext`
94
+
95
+ Returned by `client.session()`.
96
+
97
+ - `open()` — Create the paid session and connect, returns a `Stream`
98
+
99
+ ### `Stream`
100
+
101
+ Returned by `sessionContext.open()`.
102
+
103
+ - `sendAudio(data: Buffer)` — Send raw PCM16 audio
104
+ - `transcriptions()` — Async generator of `Transcription` objects
105
+ - `transcribeFile(path)` — Stream a WAV file and yield transcriptions
106
+ - `onTranscription(callback)` — Register callback for transcriptions
107
+ - `close()` — Close the stream
108
+
109
+ ### `Transcription`
110
+
111
+ - `text: string` — Transcribed text
112
+ - `start?: number` — Start time in seconds
113
+ - `end?: number` — End time in seconds
114
+ - `confidence?: number` — Confidence score
115
+ - `isFinal: boolean` — Whether this is a final transcription
116
+
117
+ ## Supported Languages
118
+
119
+ 25 languages via Parakeet-TDT (fast) with Whisper fallback:
120
+
121
+ English, Russian, German, French, Spanish, Italian, Portuguese, Dutch, Polish, Czech, Romanian, Hungarian, Greek, Turkish, Ukrainian, Swedish, Norwegian, Danish, Finnish, Catalan, Croatian, Lithuanian, Slovenian, Latvian, Estonian.
122
+
123
+ ## Error Handling
124
+
125
+ ```typescript
126
+ import { PaymentError, SessionExpiredError, ConnectionError } from "@dtelecom/stt";
127
+
128
+ try {
129
+ const stream = await client.session({ minutes: 5 }).open();
130
+ for await (const t of stream.transcriptions()) {
131
+ console.log(t.text);
132
+ }
133
+ } catch (e) {
134
+ if (e instanceof PaymentError) {
135
+ console.log("Payment failed — check wallet balance");
136
+ } else if (e instanceof SessionExpiredError) {
137
+ console.log("Session time ran out");
138
+ } else if (e instanceof ConnectionError) {
139
+ console.log("Cannot connect to server");
140
+ }
141
+ }
142
+ ```
143
+
144
+ ## License
145
+
146
+ MIT
@@ -0,0 +1,20 @@
1
+ /** PCM16 mono 16kHz constants. */
2
+ export declare const SAMPLE_RATE = 16000;
3
+ export declare const SAMPLE_WIDTH = 2;
4
+ export declare const CHANNELS = 1;
5
+ export declare const BYTES_PER_SECOND: number;
6
+ export interface WavData {
7
+ pcmData: Buffer;
8
+ sampleRate: number;
9
+ channels: number;
10
+ sampleWidth: number;
11
+ }
12
+ /**
13
+ * Load a WAV file and validate that it is PCM16, 16kHz, mono.
14
+ * Returns the raw PCM data and format info.
15
+ */
16
+ export declare function loadWav(path: string): WavData;
17
+ /** Generate PCM16 silence bytes (16kHz mono). */
18
+ export declare function silence(durationSeconds: number): Buffer;
19
+ /** Helper: sleep for ms. */
20
+ export declare function sleep(ms: number): Promise<void>;
package/dist/audio.js ADDED
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BYTES_PER_SECOND = exports.CHANNELS = exports.SAMPLE_WIDTH = exports.SAMPLE_RATE = void 0;
4
+ exports.loadWav = loadWav;
5
+ exports.silence = silence;
6
+ exports.sleep = sleep;
7
+ const node_fs_1 = require("node:fs");
8
+ const errors_js_1 = require("./errors.js");
9
+ /** PCM16 mono 16kHz constants. */
10
+ exports.SAMPLE_RATE = 16000;
11
+ exports.SAMPLE_WIDTH = 2; // 16-bit = 2 bytes
12
+ exports.CHANNELS = 1;
13
+ exports.BYTES_PER_SECOND = exports.SAMPLE_RATE * exports.SAMPLE_WIDTH * exports.CHANNELS; // 32000
14
+ /**
15
+ * Load a WAV file and validate that it is PCM16, 16kHz, mono.
16
+ * Returns the raw PCM data and format info.
17
+ */
18
+ function loadWav(path) {
19
+ let buf;
20
+ try {
21
+ buf = (0, node_fs_1.readFileSync)(path);
22
+ }
23
+ catch (e) {
24
+ if (e.code === "ENOENT") {
25
+ throw new errors_js_1.AudioFormatError(`File not found: ${path}`);
26
+ }
27
+ throw new errors_js_1.AudioFormatError(`Cannot read file: ${path}`);
28
+ }
29
+ // Parse WAV header
30
+ if (buf.length < 44) {
31
+ throw new errors_js_1.AudioFormatError("Cannot read WAV file: file too small");
32
+ }
33
+ const riff = buf.toString("ascii", 0, 4);
34
+ const wave = buf.toString("ascii", 8, 12);
35
+ if (riff !== "RIFF" || wave !== "WAVE") {
36
+ throw new errors_js_1.AudioFormatError("Cannot read WAV file: not a valid WAV");
37
+ }
38
+ // Find "fmt " chunk
39
+ let offset = 12;
40
+ let fmtOffset = -1;
41
+ let dataOffset = -1;
42
+ let dataSize = 0;
43
+ while (offset < buf.length - 8) {
44
+ const chunkId = buf.toString("ascii", offset, offset + 4);
45
+ const chunkSize = buf.readUInt32LE(offset + 4);
46
+ if (chunkId === "fmt ") {
47
+ fmtOffset = offset + 8;
48
+ }
49
+ else if (chunkId === "data") {
50
+ dataOffset = offset + 8;
51
+ dataSize = chunkSize;
52
+ break; // data is always the last chunk we care about
53
+ }
54
+ offset += 8 + chunkSize;
55
+ }
56
+ if (fmtOffset === -1) {
57
+ throw new errors_js_1.AudioFormatError("Cannot read WAV file: no fmt chunk");
58
+ }
59
+ if (dataOffset === -1) {
60
+ throw new errors_js_1.AudioFormatError("Cannot read WAV file: no data chunk");
61
+ }
62
+ const audioFormat = buf.readUInt16LE(fmtOffset); // 1 = PCM
63
+ const channels = buf.readUInt16LE(fmtOffset + 2);
64
+ const sampleRate = buf.readUInt32LE(fmtOffset + 4);
65
+ const bitsPerSample = buf.readUInt16LE(fmtOffset + 14);
66
+ const sampleWidth = bitsPerSample / 8;
67
+ if (audioFormat !== 1) {
68
+ throw new errors_js_1.AudioFormatError(`Expected PCM format (1), got ${audioFormat}. Convert with: ffmpeg -i input.wav -acodec pcm_s16le output.wav`);
69
+ }
70
+ if (sampleRate !== exports.SAMPLE_RATE) {
71
+ throw new errors_js_1.AudioFormatError(`Expected ${exports.SAMPLE_RATE}Hz, got ${sampleRate}Hz. Resample with: ffmpeg -i input.wav -ar 16000 -ac 1 output.wav`);
72
+ }
73
+ if (channels !== exports.CHANNELS) {
74
+ throw new errors_js_1.AudioFormatError(`Expected mono, got ${channels} channels. Convert with: ffmpeg -i input.wav -ac 1 output.wav`);
75
+ }
76
+ if (sampleWidth !== exports.SAMPLE_WIDTH) {
77
+ throw new errors_js_1.AudioFormatError(`Expected 16-bit, got ${bitsPerSample}-bit. Convert with: ffmpeg -i input.wav -acodec pcm_s16le output.wav`);
78
+ }
79
+ const pcmData = buf.subarray(dataOffset, dataOffset + dataSize);
80
+ return { pcmData, sampleRate, channels, sampleWidth };
81
+ }
82
+ /** Generate PCM16 silence bytes (16kHz mono). */
83
+ function silence(durationSeconds) {
84
+ const numBytes = Math.floor(exports.BYTES_PER_SECOND * durationSeconds);
85
+ return Buffer.alloc(numBytes);
86
+ }
87
+ /** Helper: sleep for ms. */
88
+ function sleep(ms) {
89
+ return new Promise((resolve) => setTimeout(resolve, ms));
90
+ }
91
+ //# sourceMappingURL=audio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio.js","sourceRoot":"","sources":["../src/audio.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ import { Stream } from "./stream.js";
2
+ import type { PricingInfo, SessionInfo } from "./types.js";
3
+ export interface STTClientOptions {
4
+ privateKey: string;
5
+ url?: string;
6
+ }
7
+ export interface SessionOptions {
8
+ minutes?: number;
9
+ language?: string;
10
+ autoExtend?: boolean;
11
+ }
12
+ /**
13
+ * Client for dTelecom real-time speech-to-text with x402 micropayments.
14
+ */
15
+ export declare class STTClient {
16
+ /** @internal */ readonly _url: string;
17
+ /** @internal */ readonly _wsUrl: string;
18
+ /** @internal */ readonly _fetchWithPayment: typeof fetch;
19
+ constructor(options: STTClientOptions);
20
+ /**
21
+ * Create a session context. Call `.open()` to connect.
22
+ */
23
+ session(options?: SessionOptions): SessionContext;
24
+ /** @internal Buy a session via x402 payment. */
25
+ _createSession(minutes: number, language: string): Promise<SessionInfo>;
26
+ /** @internal Extend session with additional paid minutes. */
27
+ _extendSession(sessionId: string, minutes?: number): Promise<Record<string, unknown>>;
28
+ /** Get pricing information (no payment required). */
29
+ pricing(): Promise<PricingInfo>;
30
+ /** Check server health (no payment required). */
31
+ health(): Promise<Record<string, unknown>>;
32
+ }
33
+ /**
34
+ * Context returned by `client.session()`.
35
+ * Call `.open()` to create the session and connect.
36
+ */
37
+ export declare class SessionContext {
38
+ private _client;
39
+ private _minutes;
40
+ private _language;
41
+ private _autoExtend;
42
+ constructor(client: STTClient, options?: SessionOptions);
43
+ /** Create the paid session and open a WebSocket stream. */
44
+ open(): Promise<Stream>;
45
+ }
package/dist/client.js ADDED
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SessionContext = exports.STTClient = void 0;
4
+ const fetch_1 = require("@x402/fetch");
5
+ const client_1 = require("@x402/evm/exact/client");
6
+ const accounts_1 = require("viem/accounts");
7
+ const errors_js_1 = require("./errors.js");
8
+ const stream_js_1 = require("./stream.js");
9
+ const DEFAULT_URL = "https://x402stt.dtelecom.org";
10
+ /**
11
+ * Client for dTelecom real-time speech-to-text with x402 micropayments.
12
+ */
13
+ class STTClient {
14
+ /** @internal */ _url;
15
+ /** @internal */ _wsUrl;
16
+ /** @internal */ _fetchWithPayment;
17
+ constructor(options) {
18
+ this._url = (options.url ?? DEFAULT_URL).replace(/\/+$/, "");
19
+ this._wsUrl = this._url
20
+ .replace("https://", "wss://")
21
+ .replace("http://", "ws://");
22
+ const signer = (0, accounts_1.privateKeyToAccount)((options.privateKey.startsWith("0x")
23
+ ? options.privateKey
24
+ : `0x${options.privateKey}`));
25
+ const client = new fetch_1.x402Client();
26
+ (0, client_1.registerExactEvmScheme)(client, { signer });
27
+ this._fetchWithPayment = (0, fetch_1.wrapFetchWithPayment)(fetch, client);
28
+ }
29
+ /**
30
+ * Create a session context. Call `.open()` to connect.
31
+ */
32
+ session(options) {
33
+ return new SessionContext(this, options);
34
+ }
35
+ /** @internal Buy a session via x402 payment. */
36
+ async _createSession(minutes, language) {
37
+ let resp;
38
+ try {
39
+ resp = await this._fetchWithPayment(`${this._url}/v1/session`, {
40
+ method: "POST",
41
+ headers: { "Content-Type": "application/json" },
42
+ body: JSON.stringify({ minutes, language }),
43
+ });
44
+ }
45
+ catch (e) {
46
+ throw new errors_js_1.ConnectionError(`Cannot reach server: ${e}`);
47
+ }
48
+ if (resp.status === 402) {
49
+ const body = await resp.json().catch(() => ({}));
50
+ throw new errors_js_1.PaymentError(`Payment failed: ${body.message ?? resp.statusText}`);
51
+ }
52
+ if (!resp.ok) {
53
+ const text = await resp.text().catch(() => "");
54
+ throw new errors_js_1.STTError(`Session creation failed (${resp.status}): ${text}`);
55
+ }
56
+ const data = (await resp.json());
57
+ return {
58
+ sessionId: data.session_id,
59
+ sessionKey: data.session_key,
60
+ wsUrl: data.ws_url ?? `${this._wsUrl}/v1/stream`,
61
+ remainingSeconds: data.remaining_seconds,
62
+ minutes: data.minutes,
63
+ priceUsd: data.price_usd,
64
+ };
65
+ }
66
+ /** @internal Extend session with additional paid minutes. */
67
+ async _extendSession(sessionId, minutes = 5) {
68
+ let resp;
69
+ try {
70
+ resp = await this._fetchWithPayment(`${this._url}/v1/session/extend`, {
71
+ method: "POST",
72
+ headers: { "Content-Type": "application/json" },
73
+ body: JSON.stringify({ session_id: sessionId, minutes }),
74
+ });
75
+ }
76
+ catch (e) {
77
+ throw new errors_js_1.ConnectionError(`Cannot reach server: ${e}`);
78
+ }
79
+ if (!resp.ok) {
80
+ const text = await resp.text().catch(() => "");
81
+ throw new errors_js_1.PaymentError(`Extend failed (${resp.status}): ${text}`);
82
+ }
83
+ return (await resp.json());
84
+ }
85
+ /** Get pricing information (no payment required). */
86
+ async pricing() {
87
+ let resp;
88
+ try {
89
+ resp = await fetch(`${this._url}/pricing`, {
90
+ signal: AbortSignal.timeout(10_000),
91
+ });
92
+ }
93
+ catch (e) {
94
+ throw new errors_js_1.ConnectionError(`Cannot reach server: ${e}`);
95
+ }
96
+ if (!resp.ok) {
97
+ throw new errors_js_1.STTError(`Pricing request failed (${resp.status})`);
98
+ }
99
+ const data = (await resp.json());
100
+ return {
101
+ pricePerMinuteUsd: data.price_per_minute_usd,
102
+ minMinutes: data.min_minutes,
103
+ maxMinutes: data.max_minutes,
104
+ minPriceUsd: data.min_price_usd ??
105
+ data.price_per_minute_usd * data.min_minutes,
106
+ currency: data.currency,
107
+ network: data.network,
108
+ };
109
+ }
110
+ /** Check server health (no payment required). */
111
+ async health() {
112
+ let resp;
113
+ try {
114
+ resp = await fetch(`${this._url}/health`, {
115
+ signal: AbortSignal.timeout(10_000),
116
+ });
117
+ }
118
+ catch (e) {
119
+ throw new errors_js_1.ConnectionError(`Cannot reach server: ${e}`);
120
+ }
121
+ return (await resp.json());
122
+ }
123
+ }
124
+ exports.STTClient = STTClient;
125
+ /**
126
+ * Context returned by `client.session()`.
127
+ * Call `.open()` to create the session and connect.
128
+ */
129
+ class SessionContext {
130
+ _client;
131
+ _minutes;
132
+ _language;
133
+ _autoExtend;
134
+ constructor(client, options) {
135
+ this._client = client;
136
+ this._minutes = options?.minutes ?? 5;
137
+ this._language = options?.language ?? "en";
138
+ this._autoExtend = options?.autoExtend ?? true;
139
+ }
140
+ /** Create the paid session and open a WebSocket stream. */
141
+ async open() {
142
+ const info = await this._client._createSession(this._minutes, this._language);
143
+ console.log(`Session created: id=${info.sessionId.slice(0, 8)}, ${info.remainingSeconds}s remaining, $${info.priceUsd}`);
144
+ const wsUrl = `${this._client._wsUrl}/v1/stream`;
145
+ const stream = new stream_js_1.Stream({
146
+ wsUrl,
147
+ sessionInfo: info,
148
+ client: this._client,
149
+ language: this._language,
150
+ autoExtend: this._autoExtend,
151
+ });
152
+ await stream._connect();
153
+ return stream;
154
+ }
155
+ }
156
+ exports.SessionContext = SessionContext;
157
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /** Base exception for all STT errors. */
2
+ export declare class STTError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ /** Payment failed or insufficient funds. */
6
+ export declare class PaymentError extends STTError {
7
+ constructor(message: string);
8
+ }
9
+ /** Session time exhausted. */
10
+ export declare class SessionExpiredError extends STTError {
11
+ constructor(message: string);
12
+ }
13
+ /** WebSocket or HTTP connection error. */
14
+ export declare class ConnectionError extends STTError {
15
+ constructor(message: string);
16
+ }
17
+ /** Invalid audio format (not PCM16, wrong sample rate, etc.). */
18
+ export declare class AudioFormatError extends STTError {
19
+ constructor(message: string);
20
+ }
package/dist/errors.js ADDED
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AudioFormatError = exports.ConnectionError = exports.SessionExpiredError = exports.PaymentError = exports.STTError = void 0;
4
+ /** Base exception for all STT errors. */
5
+ class STTError extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "STTError";
9
+ }
10
+ }
11
+ exports.STTError = STTError;
12
+ /** Payment failed or insufficient funds. */
13
+ class PaymentError extends STTError {
14
+ constructor(message) {
15
+ super(message);
16
+ this.name = "PaymentError";
17
+ }
18
+ }
19
+ exports.PaymentError = PaymentError;
20
+ /** Session time exhausted. */
21
+ class SessionExpiredError extends STTError {
22
+ constructor(message) {
23
+ super(message);
24
+ this.name = "SessionExpiredError";
25
+ }
26
+ }
27
+ exports.SessionExpiredError = SessionExpiredError;
28
+ /** WebSocket or HTTP connection error. */
29
+ class ConnectionError extends STTError {
30
+ constructor(message) {
31
+ super(message);
32
+ this.name = "ConnectionError";
33
+ }
34
+ }
35
+ exports.ConnectionError = ConnectionError;
36
+ /** Invalid audio format (not PCM16, wrong sample rate, etc.). */
37
+ class AudioFormatError extends STTError {
38
+ constructor(message) {
39
+ super(message);
40
+ this.name = "AudioFormatError";
41
+ }
42
+ }
43
+ exports.AudioFormatError = AudioFormatError;
44
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;AAAA,yCAAyC;AACzC,MAAa,QAAS,SAAQ,KAAK;IACjC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IACzB,CAAC;CACF;AALD,4BAKC;AAED,4CAA4C;AAC5C,MAAa,YAAa,SAAQ,QAAQ;IACxC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AALD,oCAKC;AAED,8BAA8B;AAC9B,MAAa,mBAAoB,SAAQ,QAAQ;IAC/C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF;AALD,kDAKC;AAED,0CAA0C;AAC1C,MAAa,eAAgB,SAAQ,QAAQ;IAC3C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AALD,0CAKC;AAED,iEAAiE;AACjE,MAAa,gBAAiB,SAAQ,QAAQ;IAC5C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AALD,4CAKC"}
@@ -0,0 +1,5 @@
1
+ export { STTClient, SessionContext } from "./client.js";
2
+ export type { STTClientOptions, SessionOptions } from "./client.js";
3
+ export { Stream } from "./stream.js";
4
+ export type { Transcription, SessionInfo, PricingInfo } from "./types.js";
5
+ export { STTError, PaymentError, SessionExpiredError, ConnectionError, AudioFormatError, } from "./errors.js";
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AudioFormatError = exports.ConnectionError = exports.SessionExpiredError = exports.PaymentError = exports.STTError = exports.Stream = exports.SessionContext = exports.STTClient = void 0;
4
+ var client_js_1 = require("./client.js");
5
+ Object.defineProperty(exports, "STTClient", { enumerable: true, get: function () { return client_js_1.STTClient; } });
6
+ Object.defineProperty(exports, "SessionContext", { enumerable: true, get: function () { return client_js_1.SessionContext; } });
7
+ var stream_js_1 = require("./stream.js");
8
+ Object.defineProperty(exports, "Stream", { enumerable: true, get: function () { return stream_js_1.Stream; } });
9
+ var errors_js_1 = require("./errors.js");
10
+ Object.defineProperty(exports, "STTError", { enumerable: true, get: function () { return errors_js_1.STTError; } });
11
+ Object.defineProperty(exports, "PaymentError", { enumerable: true, get: function () { return errors_js_1.PaymentError; } });
12
+ Object.defineProperty(exports, "SessionExpiredError", { enumerable: true, get: function () { return errors_js_1.SessionExpiredError; } });
13
+ Object.defineProperty(exports, "ConnectionError", { enumerable: true, get: function () { return errors_js_1.ConnectionError; } });
14
+ Object.defineProperty(exports, "AudioFormatError", { enumerable: true, get: function () { return errors_js_1.AudioFormatError; } });
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,yCAAwD;AAA/C,sGAAA,SAAS,OAAA;AAAE,2GAAA,cAAc,OAAA;AAElC,yCAAqC;AAA5B,mGAAA,MAAM,OAAA;AAEf,yCAMqB;AALnB,qGAAA,QAAQ,OAAA;AACR,yGAAA,YAAY,OAAA;AACZ,gHAAA,mBAAmB,OAAA;AACnB,4GAAA,eAAe,OAAA;AACf,6GAAA,gBAAgB,OAAA"}
@@ -0,0 +1,51 @@
1
+ import type { SessionInfo, Transcription } from "./types.js";
2
+ export interface StreamOptions {
3
+ wsUrl: string;
4
+ sessionInfo: SessionInfo;
5
+ client: {
6
+ _extendSession(sessionId: string, minutes?: number): Promise<Record<string, unknown>>;
7
+ };
8
+ language: string;
9
+ autoExtend: boolean;
10
+ }
11
+ /**
12
+ * WebSocket stream for sending audio and receiving transcriptions.
13
+ * Do not instantiate directly — use `STTClient.session().open()`.
14
+ */
15
+ export declare class Stream {
16
+ private _wsUrl;
17
+ private _info;
18
+ private _client;
19
+ private _language;
20
+ private _autoExtend;
21
+ private _ws;
22
+ private _closed;
23
+ private _extending;
24
+ /** Queued transcriptions for the async iterator. null = end sentinel. */
25
+ private _queue;
26
+ private _waiters;
27
+ private _callbacks;
28
+ constructor(options: StreamOptions);
29
+ /** @internal Connect WebSocket, send config, wait for ready. */
30
+ _connect(): Promise<void>;
31
+ /** Close the WebSocket connection gracefully. */
32
+ close(): Promise<void>;
33
+ /** Send raw PCM16 audio bytes (16kHz mono). */
34
+ sendAudio(data: Buffer): Promise<void>;
35
+ /** Register a callback for incoming transcriptions. */
36
+ onTranscription(callback: (t: Transcription) => void): void;
37
+ /** Async iterator yielding transcriptions as they arrive. */
38
+ transcriptions(): AsyncGenerator<Transcription>;
39
+ /**
40
+ * Stream a WAV file and yield transcriptions.
41
+ * Loads the file, streams at real-time speed, sends trailing silence,
42
+ * and yields results.
43
+ */
44
+ transcribeFile(path: string): AsyncGenerator<Transcription>;
45
+ private _push;
46
+ private _pull;
47
+ private _pullWithTimeout;
48
+ private _onMessage;
49
+ private _onClose;
50
+ private _autoExtendSession;
51
+ }
package/dist/stream.js ADDED
@@ -0,0 +1,258 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Stream = void 0;
7
+ const ws_1 = __importDefault(require("ws"));
8
+ const audio_js_1 = require("./audio.js");
9
+ const errors_js_1 = require("./errors.js");
10
+ const types_js_1 = require("./types.js");
11
+ /** How long to wait for new transcriptions after audio ends. */
12
+ const FILE_DRAIN_TIMEOUT_MS = 5_000;
13
+ /** Trailing silence to flush VAD pipeline. */
14
+ const TRAILING_SILENCE_SECONDS = 2.0;
15
+ /** Chunk size for real-time audio streaming. */
16
+ const CHUNK_MS = 20;
17
+ /**
18
+ * WebSocket stream for sending audio and receiving transcriptions.
19
+ * Do not instantiate directly — use `STTClient.session().open()`.
20
+ */
21
+ class Stream {
22
+ _wsUrl;
23
+ _info;
24
+ _client;
25
+ _language;
26
+ _autoExtend;
27
+ _ws = null;
28
+ _closed = false;
29
+ _extending = false;
30
+ /** Queued transcriptions for the async iterator. null = end sentinel. */
31
+ _queue = [];
32
+ _waiters = [];
33
+ _callbacks = [];
34
+ constructor(options) {
35
+ this._wsUrl = options.wsUrl;
36
+ this._info = options.sessionInfo;
37
+ this._client = options.client;
38
+ this._language = options.language;
39
+ this._autoExtend = options.autoExtend;
40
+ }
41
+ // ── Connection lifecycle ──────────────────────────────────────────
42
+ /** @internal Connect WebSocket, send config, wait for ready. */
43
+ async _connect() {
44
+ return new Promise((resolve, reject) => {
45
+ try {
46
+ this._ws = new ws_1.default(this._wsUrl);
47
+ }
48
+ catch (e) {
49
+ reject(new errors_js_1.ConnectionError(`WebSocket connection failed: ${e}`));
50
+ return;
51
+ }
52
+ const timeout = setTimeout(() => {
53
+ this._ws?.close();
54
+ reject(new errors_js_1.ConnectionError("Timeout waiting for ready message"));
55
+ }, 30_000);
56
+ this._ws.on("error", (err) => {
57
+ clearTimeout(timeout);
58
+ reject(new errors_js_1.ConnectionError(`WebSocket connection failed: ${err.message}`));
59
+ });
60
+ this._ws.on("open", () => {
61
+ const config = {
62
+ type: "config",
63
+ language: this._language,
64
+ session_key: this._info.sessionKey,
65
+ };
66
+ this._ws.send(JSON.stringify(config));
67
+ });
68
+ // Wait for the "ready" message before resolving
69
+ this._ws.once("message", (raw) => {
70
+ clearTimeout(timeout);
71
+ const msg = JSON.parse(raw.toString());
72
+ if (msg.type === "error") {
73
+ this._ws.close();
74
+ reject(new errors_js_1.STTError(`Server error: ${msg.message ?? JSON.stringify(msg)}`));
75
+ return;
76
+ }
77
+ if (msg.type !== "ready") {
78
+ this._ws.close();
79
+ reject(new errors_js_1.STTError(`Expected ready message, got: ${JSON.stringify(msg)}`));
80
+ return;
81
+ }
82
+ const remaining = msg.remaining_seconds ?? this._info.remainingSeconds;
83
+ console.log(`Stream ready, remaining=${Math.round(remaining)}s`);
84
+ // Switch to the persistent recv loop
85
+ this._ws.on("message", (data, isBinary) => this._onMessage(data, isBinary));
86
+ this._ws.on("close", () => this._onClose());
87
+ resolve();
88
+ });
89
+ });
90
+ }
91
+ /** Close the WebSocket connection gracefully. */
92
+ async close() {
93
+ if (this._closed)
94
+ return;
95
+ this._closed = true;
96
+ // Signal end to async iterators
97
+ this._push(null);
98
+ if (this._ws) {
99
+ try {
100
+ this._ws.close();
101
+ }
102
+ catch {
103
+ // ignore
104
+ }
105
+ }
106
+ }
107
+ // ── Sending audio ─────────────────────────────────────────────────
108
+ /** Send raw PCM16 audio bytes (16kHz mono). */
109
+ async sendAudio(data) {
110
+ if (this._closed)
111
+ throw new errors_js_1.STTError("Stream is closed");
112
+ this._ws.send(data);
113
+ }
114
+ // ── Receiving transcriptions ──────────────────────────────────────
115
+ /** Register a callback for incoming transcriptions. */
116
+ onTranscription(callback) {
117
+ this._callbacks.push(callback);
118
+ }
119
+ /** Async iterator yielding transcriptions as they arrive. */
120
+ async *transcriptions() {
121
+ while (true) {
122
+ const item = await this._pull();
123
+ if (item === null)
124
+ return;
125
+ yield item;
126
+ }
127
+ }
128
+ /**
129
+ * Stream a WAV file and yield transcriptions.
130
+ * Loads the file, streams at real-time speed, sends trailing silence,
131
+ * and yields results.
132
+ */
133
+ async *transcribeFile(path) {
134
+ const { pcmData } = (0, audio_js_1.loadWav)(path);
135
+ const audioDuration = pcmData.length / audio_js_1.BYTES_PER_SECOND;
136
+ console.log(`Streaming file ${path} (${audioDuration.toFixed(1)}s)`);
137
+ // Stream audio at real-time speed
138
+ const chunkBytes = Math.floor(audio_js_1.BYTES_PER_SECOND * CHUNK_MS / 1000);
139
+ let offset = 0;
140
+ while (offset < pcmData.length) {
141
+ const chunk = pcmData.subarray(offset, offset + chunkBytes);
142
+ this._ws.send(chunk);
143
+ offset += chunkBytes;
144
+ await (0, audio_js_1.sleep)(CHUNK_MS);
145
+ }
146
+ // Send trailing silence to flush VAD
147
+ this._ws.send((0, audio_js_1.silence)(TRAILING_SILENCE_SECONDS));
148
+ await (0, audio_js_1.sleep)(TRAILING_SILENCE_SECONDS * 1000);
149
+ // Drain transcriptions with timeout
150
+ while (true) {
151
+ const item = await this._pullWithTimeout(FILE_DRAIN_TIMEOUT_MS);
152
+ if (item === null)
153
+ return;
154
+ yield item;
155
+ }
156
+ }
157
+ // ── Internal queue helpers ────────────────────────────────────────
158
+ _push(item) {
159
+ this._queue.push(item);
160
+ // Wake up any waiters
161
+ const waiter = this._waiters.shift();
162
+ if (waiter)
163
+ waiter();
164
+ }
165
+ async _pull() {
166
+ if (this._queue.length > 0) {
167
+ return this._queue.shift();
168
+ }
169
+ await new Promise((resolve) => this._waiters.push(resolve));
170
+ return this._queue.shift() ?? null;
171
+ }
172
+ async _pullWithTimeout(timeoutMs) {
173
+ if (this._queue.length > 0) {
174
+ return this._queue.shift();
175
+ }
176
+ return new Promise((resolve) => {
177
+ const timer = setTimeout(() => {
178
+ // Remove the waiter if it hasn't been called
179
+ const idx = this._waiters.indexOf(wakerFn);
180
+ if (idx >= 0)
181
+ this._waiters.splice(idx, 1);
182
+ resolve(null);
183
+ }, timeoutMs);
184
+ const wakerFn = () => {
185
+ clearTimeout(timer);
186
+ resolve(this._queue.shift() ?? null);
187
+ };
188
+ this._waiters.push(wakerFn);
189
+ });
190
+ }
191
+ // ── WebSocket message handling ────────────────────────────────────
192
+ _onMessage(raw, isBinary) {
193
+ // Ignore binary messages from server
194
+ if (isBinary)
195
+ return;
196
+ const text = raw.toString();
197
+ let msg;
198
+ try {
199
+ msg = JSON.parse(text);
200
+ }
201
+ catch {
202
+ return;
203
+ }
204
+ const msgType = msg.type;
205
+ if (msgType === "transcription") {
206
+ const t = (0, types_js_1.transcriptionFromMessage)(msg);
207
+ this._push(t);
208
+ for (const cb of this._callbacks) {
209
+ try {
210
+ cb(t);
211
+ }
212
+ catch (e) {
213
+ console.error("Transcription callback error:", e);
214
+ }
215
+ }
216
+ }
217
+ else if (msgType === "session_expiring") {
218
+ const remaining = msg.remaining_seconds;
219
+ console.warn(`Session expiring, ${Math.round(remaining)}s remaining`);
220
+ if (this._autoExtend && !this._extending) {
221
+ this._autoExtendSession();
222
+ }
223
+ }
224
+ else if (msgType === "session_extended") {
225
+ const remaining = msg.remaining_seconds;
226
+ console.log(`Session extended, ${Math.round(remaining)}s remaining`);
227
+ }
228
+ else if (msgType === "session_expired") {
229
+ console.error("Session expired");
230
+ this._push(null);
231
+ }
232
+ else if (msgType === "error") {
233
+ console.error(`Server error: ${msg.message ?? JSON.stringify(msg)}`);
234
+ }
235
+ }
236
+ _onClose() {
237
+ if (!this._closed) {
238
+ this._push(null);
239
+ }
240
+ }
241
+ async _autoExtendSession() {
242
+ this._extending = true;
243
+ try {
244
+ const result = await this._client._extendSession(this._info.sessionId, 5);
245
+ console.log(`Auto-extended session: +${result.minutes_added ?? 5}min, ` +
246
+ `remaining=${Math.round(result.remaining_seconds)}s, ` +
247
+ `$${result.price_usd ?? "?"}`);
248
+ }
249
+ catch (e) {
250
+ console.error("Auto-extend failed:", e);
251
+ }
252
+ finally {
253
+ this._extending = false;
254
+ }
255
+ }
256
+ }
257
+ exports.Stream = Stream;
258
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /** A transcription result from the STT server. */
2
+ export interface Transcription {
3
+ text: string;
4
+ start?: number;
5
+ end?: number;
6
+ confidence?: number;
7
+ isFinal: boolean;
8
+ }
9
+ /** Info returned when a session is created. */
10
+ export interface SessionInfo {
11
+ sessionId: string;
12
+ sessionKey: string;
13
+ wsUrl: string;
14
+ remainingSeconds: number;
15
+ minutes: number;
16
+ priceUsd: string;
17
+ }
18
+ /** Pricing information from the server. */
19
+ export interface PricingInfo {
20
+ pricePerMinuteUsd: number;
21
+ minMinutes: number;
22
+ maxMinutes: number;
23
+ minPriceUsd: number;
24
+ currency: string;
25
+ network: string;
26
+ }
27
+ /** Parse a server JSON message into a Transcription. */
28
+ export declare function transcriptionFromMessage(msg: Record<string, unknown>): Transcription;
package/dist/types.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.transcriptionFromMessage = transcriptionFromMessage;
4
+ /** Parse a server JSON message into a Transcription. */
5
+ function transcriptionFromMessage(msg) {
6
+ return {
7
+ text: msg.text ?? "",
8
+ start: msg.start,
9
+ end: msg.end,
10
+ confidence: msg.confidence,
11
+ isFinal: msg.is_final ?? true,
12
+ };
13
+ }
14
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;AA8BA,4DAQC;AATD,wDAAwD;AACxD,SAAgB,wBAAwB,CAAC,GAA4B;IACnE,OAAO;QACL,IAAI,EAAG,GAAG,CAAC,IAAe,IAAI,EAAE;QAChC,KAAK,EAAE,GAAG,CAAC,KAA2B;QACtC,GAAG,EAAE,GAAG,CAAC,GAAyB;QAClC,UAAU,EAAE,GAAG,CAAC,UAAgC;QAChD,OAAO,EAAG,GAAG,CAAC,QAAoB,IAAI,IAAI;KAC3C,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@dtelecom/stt",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript SDK for dTelecom real-time speech-to-text with x402 micropayments",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "prepublishOnly": "tsc"
13
+ },
14
+ "keywords": [
15
+ "stt",
16
+ "speech-to-text",
17
+ "x402",
18
+ "micropayments",
19
+ "realtime"
20
+ ],
21
+ "author": "dTelecom <dev@dtelecom.org>",
22
+ "license": "MIT",
23
+ "dependencies": {
24
+ "@x402/evm": "^2.0.0",
25
+ "@x402/fetch": "^2.0.0",
26
+ "viem": "^2.0.0",
27
+ "ws": "^8.18.0"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^22.0.0",
31
+ "@types/ws": "^8.5.0",
32
+ "tsx": "^4.0.0",
33
+ "typescript": "^5.7.0"
34
+ }
35
+ }