npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@humain-voice/sdk

v0.16.1

Published

TypeScript SDK for HUMAIN Voice ASR and TTS services

Readme

HUMAIN Voice JavaScript/TypeScript SDK

Official TS/JS SDK for HUMAIN Voice ASR and TTS services. It supports:

  • Fast Transcription (file-based)
  • Realtime (streaming audio via websockets)
  • Batch Transcription (HTTP polling)
  • Live Diarization
  • ASR subtitle helpers for SRT and WebVTT
  • Text-to-Speech (websocket streaming, Node/Bun)

Installation

Using npm:

npm install @humain-voice/sdk

Using pnpm:

pnpm add @humain-voice/sdk

Using bun:

bun add @humain-voice/sdk

Use @humain-voice/sdk for new code. The deprecated compatibility package still works for now, but emits a deprecation warning and re-exports this package.

Changelog

Release notes are maintained in the repository root CHANGELOG.md. The npm README is generated during publish and includes the full changelog. The package also ships CHANGELOG.md.

Configuration

Provide environment variables at runtime:

  • API_URL: Base URL of the ASR service (e.g. https://api.example.com)
  • API_PATH: Socket.IO path for the service you are calling. Required, with no default — each service has its own path (e.g. /realtime/socket.io for realtime, /fast-transcription/socket.io for fast transcription, /tts/socket.io for TTS). It must be passed to every client.
  • API_KEY: Your API key

Example (Bash — the path below is the realtime example):

export API_URL="https://api.example.com"
export API_PATH="/realtime/socket.io"
export API_KEY="<your_api_key>"

Fast Transcription

import { ASRModel, FastTranscriptionClient, Language } from '@humain-voice/sdk';
import { readFile } from 'node:fs/promises';

async function main() {
	const apiUrl = process.env.API_URL ?? '';
	const apiPath = process.env.API_PATH ?? '';
	const apiKey = process.env.API_KEY ?? '';

	const client = new FastTranscriptionClient({
		api_url: apiUrl,
		api_path: apiPath,
		api_key: apiKey,
		onConnect: () => console.info('connected'),
		onFileUpload: (resp) => console.info('file upload:', resp),
		onError: (err) => console.error('error:', err),
	});

	await client.connect();
	const audio = await readFile('samples/king_fahad_test.wav');

	const result = await client.transcribe(
		audio,
		Language.ArEn,
		ASRModel.BayanArEn,
		{ onResponse: (resp) => console.info('response:', resp) }
	);
	console.info('final result:', result);

	await client.close();
}

main().catch(console.error);

Realtime Streaming

import { Language, RealtimeClient, RealtimeStream } from '@humain-voice/sdk';
import { readFile } from 'node:fs/promises';

async function run() {
	const apiUrl = process.env.API_URL ?? '';
	const apiPath = process.env.API_PATH ?? '';
	const apiKey = process.env.API_KEY ?? '';

	const client = new RealtimeClient({
		api_url: apiUrl,
		api_path: apiPath,
		api_key: apiKey,
	});
	const stream = await client.startStream(Language.ArEn, {
		onConnect: () => console.info('connected'),
		onDisconnect: () => console.info('disconnected'),
		onResponse: (t) => console.info('response:', t),
		onError: (e) => console.error('error:', e),
	});

	const audioBytes = await readFile('samples/king_fahad_test.wav');
	const bytes = new Uint8Array(
		audioBytes.buffer,
		audioBytes.byteOffset,
		audioBytes.byteLength
	);

	const sampleRate = 16000; // Hz
	const bytesPerSample = 2; // 16-bit PCM
	const chunkDurationSec = 0.1;
	const chunkSize = Math.floor(sampleRate * bytesPerSample * chunkDurationSec);

	for (let start = 0; start < bytes.length; start += chunkSize) {
		const end = Math.min(start + chunkSize, bytes.length);
		const chunk = bytes.subarray(start, end);
		await stream.send(chunk);
		await new Promise((r) => setTimeout(r, chunkDurationSec * 1000));
	}

	await stream.close(1);
}

run().catch(console.error);

Parallel streams (single client)

import { Language, RealtimeClient, RealtimeStream } from '@humain-voice/sdk';
import { readFile } from 'node:fs/promises';

async function streamAudio(stream: RealtimeStream, audioBytes: Uint8Array) {
	const sampleRate = 16000;
	const bytesPerSample = 2;
	const chunkDurationSec = 0.1;
	const chunkSize = Math.floor(sampleRate * bytesPerSample * chunkDurationSec);

	for (let start = 0; start < audioBytes.length; start += chunkSize) {
		const end = Math.min(start + chunkSize, audioBytes.length);
		const chunk = audioBytes.subarray(start, end);
		await stream.send(chunk);
		await new Promise((r) => setTimeout(r, chunkDurationSec * 1000));
	}

	await stream.close(1);
}

async function runParallel() {
	const apiUrl = process.env.API_URL ?? '';
	const apiPath = process.env.API_PATH ?? '';
	const apiKey = process.env.API_KEY ?? '';

	const client = new RealtimeClient({
		api_url: apiUrl,
		api_path: apiPath,
		api_key: apiKey,
	});
	const streamA = await client.startStream(Language.ArEn, {
		onResponse: (t) => console.info('stream A:', t),
	});
	const streamB = await client.startStream(Language.ArEn, {
		onResponse: (t) => console.info('stream B:', t),
	});

	const audioBytes = await readFile('samples/king_fahad_test.wav');
	const bytes = new Uint8Array(
		audioBytes.buffer,
		audioBytes.byteOffset,
		audioBytes.byteLength
	);

	await Promise.all([streamAudio(streamA, bytes), streamAudio(streamB, bytes)]);
}

runParallel().catch(console.error);

Live Diarization

A standalone RealtimeDiarizationClient identifies speakers in real time over the same realtime socket, accumulating a full per-speaker timeline as audio arrives.

import {
  DIARIZATION_RECOMMENDED_CHUNK_BYTES,
  RealtimeDiarizationClient,
  type DiarizationUpdate,
} from '@humain-voice/sdk';
import { readFile } from 'node:fs/promises';

async function run() {
  const client = new RealtimeDiarizationClient({
    api_url: process.env.API_URL ?? '',
    api_key: process.env.API_KEY ?? '',
  });

  const stream = await client.startStream({
    onError: (err) => console.error('stream error:', err),
  });

  // feed audio and consume updates concurrently
  const feeder = (async () => {
    const wav = await readFile('samples/king_fahad_test.wav');
    // skip 44-byte WAV header; payload must be raw PCM16LE @ 16 kHz mono
    const pcm = new Uint8Array(wav.buffer, wav.byteOffset + 44, wav.byteLength - 44);
    for (let off = 0; off < pcm.length; off += DIARIZATION_RECOMMENDED_CHUNK_BYTES) {
      await stream.send(pcm.subarray(off, off + DIARIZATION_RECOMMENDED_CHUNK_BYTES));
      await new Promise((r) => setTimeout(r, 480)); // 480 ms real-time pace
    }
    const timeline = await stream.close();
    console.log('final timeline:', timeline);
  })();

  for await (const update of stream) {
    const u: DiarizationUpdate = update;
    console.log(
      `segments=${u.segments.length} newlyFinalized=${u.newlyFinalized.length} ` +
      `actives=${u.activeSegments.length} final=${u.isFinal}`,
    );
  }

  await feeder;
}

run().catch(console.error);

Notes:

  • Stream raw PCM16LE mono 16 kHz. The recommended chunk size is 15360 bytes (480 ms = one model inference step). Other sizes are accepted but produce a less steady result cadence.
  • final_segments arrive as per-response deltas; the SDK accumulates them. update.segments is the full best-known timeline; update.newlyFinalized is the per-response delta.
  • Active segments may be revised on any update and may overlap (simultaneous speakers).
  • stream.close() sends the terminator frame, waits up to 5 s, and returns the best-known timeline instead of throwing on timeout.

Subtitles (SRT / VTT)

Fast, realtime, and batch ASR responses include timed word offsets. The subtitle helpers group those words into readable cues and render SRT or WebVTT without adding speaker labels.

Fast and batch responses can be converted with Subtitles.fromResponse():

import { Subtitles } from '@humain-voice/sdk';

const result = await ftClient.transcribe(...);
const srt = Subtitles.fromResponse(result).toSrt();
const vtt = Subtitles.fromResponse(result).toVtt();

const batchResult = await batchClient.transcribe(...);
const batchSrt = Subtitles.fromResponse(batchResult).toSrt();

For one-shot conversion, use the exported helpers:

import { toSrt, toVtt } from '@humain-voice/sdk';

const srt = toSrt(result);
const vtt = toVtt(batchResult);

Realtime streams can collect stable final responses automatically:

import { RealtimeSubtitles } from '@humain-voice/sdk';

const captions = new RealtimeSubtitles();
const stream = await rtClient.startStream(Language.ArEn, { subtitles: captions });

// send audio, then close the stream
await stream.close();
const vtt = captions.toVtt();

Lower-level helpers are available when you already have words or cues:

import { cuesToSrt, cuesToVtt, wordsToCues } from '@humain-voice/sdk';

const cues = wordsToCues(result.words);
const srt = cuesToSrt(cues);
const vtt = cuesToVtt(cues);

Speaker labels are never rendered. When batch offsets include speaker metadata, speaker changes are used only as cue boundaries.

Defaults follow common caption readability heuristics:

  • up to two lines per cue
  • 42 characters per line
  • splits on sentence endings, long pauses, long cues, and speaker changes
  • forgiving cleanup for common ASR timing issues

Use { strict: true } when you want validation errors instead of normalization:

const captions = Subtitles.fromResponse(result, { strict: true });
const vtt = captions.toVtt({ strict: true });

Strict validation applies to both SRT and VTT output.

Object render methods are intentionally named as conversions: toSrt() and toVtt(). The lower-level helpers keep input-specific names such as cuesToSrt() and wordsToCues().

Text-to-Speech (TTS)

import { TTSClient, TtsModel } from '@humain-voice/sdk';
import { writeFile } from 'node:fs/promises';

async function tts() {
	const apiUrl = process.env.API_URL ?? '';
	const apiPath = process.env.API_PATH ?? '';
	const apiKey = process.env.API_KEY ?? '';

	const client = new TTSClient({
		api_url: apiUrl,
		api_path: apiPath,
		api_key: apiKey,
	});

	// Optional: fetch available voices (UUID + label)
	const voices = await client.listVoices({ timeoutSeconds: 5 });
	if (voices.length === 0) {
		throw new Error('No voices available');
	}

	// Single-shot bytes
	const bytes = await client.synthesize('Hello from HUMAIN Voice TTS', {
		voice_id: voices[0].id,
		model: TtsModel.Nebula,
	});
	await writeFile('out_single.pcm', bytes);

	// Streaming iterator
	const chunks: Uint8Array[] = [];
	for await (const r of client.synthesizeStream(
		'Streaming sample from HUMAIN Voice TTS',
		{
			voice_id: voices[0].id,
			model: TtsModel.Nebula,
		}
	)) {
		chunks.push(r.audio);
	}

	await client.close();
}

tts().catch(console.error);

Error handling

The SDK exposes a structured error contract aligned with the platform's ErrorResponse. The fields id, message, code, retryable, and timestamp are all optional. The error callback always fires; whether an in-flight context is terminated depends on the code's ownership (see ADR-0003).

Error code constants

import {
	ASR_TRANSCRIPTION_FAILED,
	ASR_MODEL_UNAVAILABLE,
	RATE_LIMIT_EXCEEDED,
	TTS_VOICE_LIST_FAILED,
	SERVER_INTERNAL,
	isAsrCode,
	isTtsCode,
	isRequestScopedCode,
	isRealtimeOwned,
	isTtsOwned,
} from '@humain-voice/sdk'

Unknown future codes pass through as strings. Legacy aliases such as RATE_LIMITED, VALIDATION_FAILED, and INTERNAL_ERROR remain exported for older deployments.

Socket.IO errors (TTS / realtime STT / fast STT)

The onError callback always receives an ErrorResponse. Branch on code to decide your policy:

import { ASR_MODEL_UNAVAILABLE, ASR_TRANSCRIPTION_FAILED } from '@humain-voice/sdk'

const onError = (err) => {
	console.log(`code=${err.code} retryable=${err.retryable} msg=${err.message}`)
	if (err.code === ASR_MODEL_UNAVAILABLE) {
		// Safe to retry later.
	} else if (err.code === ASR_TRANSCRIPTION_FAILED) {
		// Terminal — no retry.
	}
}

The realtime adapter only terminates an ASR stream context for ASR-shaped codes. A TTS_VOICE_LIST_FAILED arriving on the realtime socket fires the global onError but does not kill the stream.

Batch transcription HTTP errors

BatchTranscribeError (and its subclasses) carry a structured payload plus per-field accessors:

import {
	BatchTranscribeClient,
	BatchTranscribeError,
	BatchTranscribeRateLimitError,
} from '@humain-voice/sdk'

try {
	const result = await client.submit(audioBytes, 'ar')
} catch (err) {
	if (err instanceof BatchTranscribeRateLimitError) {
		console.log(`rate-limited; retryAfter=${err.retryAfter}s capacity=${err.capacity}`)
	} else if (err instanceof BatchTranscribeError) {
		console.log(`status=${err.statusCode}`)
		console.log(`code=${err.code} retryable=${err.retryable}`)
		console.log(`detail=${err.detail} jobId=${err.jobId}`)
		console.log('rawBody=', err.rawBody) // always preserved
	}
}

User-facing message preference: detail → message → error → raw text. err.message already follows that order.

Note. The maxRetries constructor option is deprecated and is a no-op. The SDK never retries — callers decide policy based on err.retryable, err.code, and err.capacity.

Types

Common enums are exported from the root entrypoint, e.g. Language, ASRModel.

Examples

See javascript/examples/ft_client.ts and javascript/examples/rt_client.ts for full runnable samples with subtitle output. Also see javascript/examples/tts_client.ts for TTS sample. The batch example (javascript/examples/batch_transcribe_client.ts) shows structured-error handling and SRT/VTT rendering end-to-end. javascript/examples/diarization_client.ts demonstrates live diarization streaming (feeder + async-iterator pattern).


Changelog

All notable changes to the HUMAIN Voice SDK (Python humain-voice, JS @humain-voice/sdk, Go module) are documented here. The format follows Keep a Changelog, and the project adheres to Semantic Versioning.

Versions are shared across all three language packages; a release tags python/vX.Y.Z, javascript/vX.Y.Z, and golang/vX.Y.Z together. The Teams release notification posts the section matching the tag's version, so keep the top section here current before tagging.

[Unreleased]

[0.16.1-beta.2] - 2026-06-18

Changed

  • Rebranded repository, package, and release documentation to HUMAIN Voice across the root, Python, JavaScript, and Go docs.
  • Python, JavaScript, and Go examples, comments, and release metadata now use HUMAIN Voice wording instead of the old product name.
  • The deprecated JavaScript compatibility package now emits HUMAIN_VOICE_COMPAT_PACKAGE_DEPRECATED and uses neutral compatibility wording in its warning message.
  • The legacy Python import path now emits HumainVoiceCompatibilityWarning with neutral compatibility wording.
  • Example package manifests now depend on the registry package names instead of local source paths that expose old package-directory names.
  • Archived live-diarization planning docs were condensed so the public docs no longer preserve obsolete package paths.

[0.16.1-beta.1] - 2026-06-18

Added

  • The Python SDK can now be imported as humain_voice, matching the humain-voice distribution name.
  • The deprecated JavaScript compatibility package now wraps @humain-voice/sdk.
  • PyPI and npm publish jobs now generate registry READMEs that append this root changelog, so package registry pages show release notes from the same source as the repository.
  • The Go module now includes a checked-in golang/CHANGELOG.md mirror so tagged Go source includes release notes for Go consumers and pkg.go.dev.

Changed

  • JavaScript examples now import @humain-voice/sdk.
  • Go dependencies were refreshed and the Go CI/container baseline moved to Go 1.25.
  • Go README and release notes no longer reference the non-existent pkg/fasttranscription compatibility wrapper.

Deprecated

  • The legacy Python import path remains available for compatibility, but now emits a deprecation warning. New code should import from humain_voice.
  • The legacy JavaScript compatibility package remains available, but now emits a deprecation warning. New code should import from @humain-voice/sdk.

[0.16.1] - 2026-06-15

Changed

  • We now recommend selecting the Arabic/English model with the unversioned BayanArEn constant instead of pinning to BayanArEnV1. The unversioned constant always maps to the current production model, so you automatically benefit from accuracy improvements without a code change. If you have a specific reason to stay on a fixed model version, the BayanArEnV1 and BayanArEnV2 constants remain available — nothing you ship today breaks.

[0.16.0] - 2026-06-11

⚠️ Breaking Changes

  • ASR and diarization model identifiers were renamed. The SDK constants used to select a model changed, so any code that selects a model must be updated before upgrading. Functionality is unchanged; these are the same models under new, brand-aligned names. Always select models through the SDK constants below rather than raw strings. Migration from 0.15.x:

    | What you used (0.15.x) | New constant | | ---------------------- | -------------------------- | | ASRModel.Ar_1 | ASRModel.NidaAr | | ASRModel.Ar_2 | ASRModel.BayanAr | | ASRModel.Ar_8k_1 | ASRModel.NidaArTelephony | | ASRModel.ArEn | ASRModel.BayanArEn | | ASRModel.ArEn_1 | ASRModel.BayanArEnV1 | | ASRModel.ArEn_2 | ASRModel.BayanArEnV2 | | BatchDiarization.M1 | BatchDiarization.D1 | | BatchDiarization.M2 | BatchDiarization.D2 |

    Go callers use the package-prefixed forms (e.g. ASRModelBayanArEnV1, BatchDiarizationD1). The old constants are no longer accepted — code using them will need to migrate to the new names.

  • The batch transcription client no longer retries failed requests automatically. Previous versions retried internally (via tenacity in Python and p-retry in JS). The client now makes exactly one HTTP call per operation and raises immediately on failure. The max_retries / maxRetries constructor option is kept for compatibility but is now a no-op; passing a non-zero value emits a deprecation warning. If you depend on retries, you must now implement your own policy — every raised error exposes retryable, retry_after, and capacity (for HTTP 429) so you can back off correctly.

  • Error handling moved to a structured, typed exception hierarchy. Errors are now routed by error code to the client that owns them, and failures raise a specific exception type instead of a generic error. For batch transcription: HTTP 401 raises an auth error, HTTP 429 raises a rate-limit error (carrying retry_after and capacity), and all other failures raise the base BatchTranscribeError. The full server payload is available on the exception (payload / raw_body) for inspection. If you currently catch a broad error type or parse error strings, switch to catching the typed exceptions and reading their attributes.

Added

  • Realtime speaker diarization over the streaming transport — receive per-speaker labels on transcripts as audio streams in.
  • SRT and VTT subtitle helpers in Python, JS, and Go to turn transcription results directly into ready-to-use subtitle files.

[0.15.1] - 2026-05-13

Added

  • Official Go SDK, at full feature parity with the Python and JavaScript packages: realtime speech-to-text, fast transcription, text-to-speech, and batch transcription, all over the same streaming transport. If you work in Go, you no longer need to call the platform API directly.

[0.15.0] - 2026-05-06

⚠️ Breaking Changes

  • Client imports, event names, and configuration shapes changed. Internally the STT and TTS clients were rebuilt on a shared connection layer, and that shifted parts of the public surface — some import paths, streaming event names, and configuration option shapes are different. Review your client setup and event handlers against the updated examples when upgrading from 0.14.x.
  • disconnect_socketio was removed. Connection teardown is now handled by the client itself; if you imported disconnect_socketio directly, drop the call — closing the client cleans up the connection for you.

Changed

  • STT and TTS clients now share a single connection and lifecycle, which makes connection handling more consistent and predictable across both. This is an internal change, but it is the reason for the surface adjustments noted above.

[0.14.5] - 2026-02-03

Added

  • Run multiple concurrent streams from a single RealtimeClient. You can now open several independent realtime sessions from one client instance instead of constructing a separate client per stream.

Changed

  • Invalid ASR and TTS request fields are now rejected up front. The SDK validates request fields — including voice-reference inputs for TTS — before sending, so mistakes surface immediately as a clear client-side error rather than failing partway through a request. Requests that previously appeared to start before failing server-side may now raise a validation error sooner; fix the flagged field and the request proceeds as normal.