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

@anov/sherpa-web

v1.1.1

Published

Next-gen Sherpa-onnx Web/Wasm audio toolkit (Production-ready STT & VAD; TTS & Diarization in Roadmap)

Readme

@anov/sherpa-web

浏览器端轻量高性能 Sherpa-onnx WebAssembly 语音音频工具箱。提供生产级流式 ASR(Zipformer INT8)、端侧人声活动检测(Silero-VAD)与 Two-Pass 离线高精终审(SenseVoice INT8),支持热词动态加权、近音自愈与纯离线推理。

npm version license

Context Pointers

  • 资产清单与离线部署: 详见 ASSETS.md(获取全量模型 SHA-256 校验码、CDN 镜像直链与自动化下载脚本)。
  • 意图与决策引擎: 搭配使用 @anov/intent-matcher 构建四环离线语义理解与多轮对话系统。
  • 架构术语规范: 遵循 GLOSSARY.md。

⚠️ 关键前置要求:跨域隔离 (COOP / COEP)

底层多线程 WebAssembly 强依赖 SharedArrayBuffer。网页服务器必须配置两项响应头,否则浏览器将拦截内存分配:

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
  • Vite 开发环境:配置 vite-plugin-cross-origin-isolation 插件。
  • Nginx 生产环境:在相应 location / 块中添加上述两条 add_header 指令。

🚀 核心工作流 (In-File Workflow)

步骤 1:安装依赖

pnpm add @anov/sherpa-web

步骤 2:实例化识别器并启动录音

import { OnlineRecognizer } from '@anov/sherpa-web';

const asr = new OnlineRecognizer({
  hotwords: ['打开控制台', '播放音乐'],
  hotwordsScore: 2.5,
  callbacks: {
    onPartial: (text) => console.log('流式字幕:', text),
    onFinal: (text, meta) => {
      console.log('句子终审定稿:', text);
      console.log('来源阶段:', meta?.pass === 2 ? 'SenseVoice 终审' : 'Zipformer 流式');
    },
    onStatusChange: (status) => console.log('引擎状态:', status),
  },
});

// 1. 初始化运行时并预热核心模型
await asr.init();

// 2. 请求麦克风并开启实时识别
await asr.start();

// 3. 停止识别并注销音频管道
// await asr.stop();

验收判据 (Completion Criterion):调用 await asr.start() 后麦克风指示灯点亮;对着麦克风发音时,onPartial 输出递增文本,停顿后 onFinal 返回定稿文本。


🔀 核心分支场景 (Branch Recipes)

分支 1:运行时无损动态更新热词 (Dynamic Hotwords)

支持在录音过程中动态更新业务热词。若正在收音,识别器自动排队至当前语音段结束后安全重构,绝不打断当前录音:

// 格式支持字符串、带权值分数字符串或对象
await asr.setHotwords([
  { word: '控制中心', score: 3.0 },
  '静音模式 :2.5',
  '返回主页',
]);

分支 2:启用 Two-Pass 离线高精终审 (SenseVoice Rescoring)

第一阶段流式 Zipformer 保证 50ms 级首字响应;句子结束时自动触发第二阶段 SenseVoice INT8 模型(239MB)修正文本与回填富标点:

const asr = new OnlineRecognizer({
  twoPass: {
    mode: 'auto',              // 'auto' 根据设备 CPU 核心与内存自适应启用
    modelType: 'sense-voice',
    modelPaths: {
      modelPath: '/sense-voice.onnx',
      tokensPath: '/sense-voice-tokens.txt',
    },
  },
  callbacks: {
    onFinal: (text, meta) => {
      // meta.isRescored 为 true 表示经过第二阶段修正
      console.log(`[Pass ${meta?.pass}] 最终结果: ${text}`);
    },
  },
});

await asr.init();

分支 3:独立使用 Silero-VAD 人声活动检测 (Standalone VAD)

无需初始化 ASR 即可独立运行人声起止点检测,原生支持多播订阅与异常隔离:

import { VadDetector } from '@anov/sherpa-web';

const vad = new VadDetector({
  threshold: 0.015,
  minSpeechDurationMs: 60,
  minSilenceDurationMs: 700,
});

await vad.init();

// 多播事件订阅(返回取消订阅函数)
const unsubscribe = vad.subscribe({
  onSpeechStart: () => console.log('检测到人声起始'),
  onSpeechEnd: (segment) => console.log('人声结束,时长:', segment.durationSec),
});

// 向 VAD 推送 16kHz PCM 单声道切片
vad.acceptWaveform(pcmChunk);

// 注销
// unsubscribe();
// vad.dispose();

分支 4:纯离线 / 专网环境加载 (Air-gapped Deployment)

在无法连通外网的专网环境中,指定 localBase: '/' 即可强行阻断所有 CDN 外部请求:

const asr = new OnlineRecognizer({
  modelPaths: {
    localBase: '/', // 从站点 public 静态根目录加载 wasm 与 data
  },
  twoPass: {
    mode: 'auto',
    modelType: 'sense-voice',
    modelPaths: {
      modelPath: '/sense-voice.onnx',
      tokensPath: '/sense-voice-tokens.txt',
    },
  },
});

分支 5:脱离 Wasm / 浏览器的单元测试 (Zero-Wasm Headless Testing)

在 Node.js 或 CI 环境中,利用 FakeSherpaRuntime 与 BufferAudioSource 运行毫秒级单测,无需加载数十兆二进制:

import { describe, it, expect } from 'vitest';
import { OnlineRecognizer, FakeSherpaRuntime, BufferAudioSource } from '@anov/sherpa-web';

describe('ASR 业务流单测', () => {
  it('验证音频切片消费与定稿输出', async () => {
    let finalOutput = '';
    const fakeRuntime = new FakeSherpaRuntime();
    fakeRuntime.setMockResult('测试语音指令');

    const asr = new OnlineRecognizer({
      runtime: fakeRuntime,
      audioSource: new BufferAudioSource(new Float32Array(16000)),
      callbacks: {
        onFinal: (text) => { finalOutput = text; },
      },
    });

    await asr.init();
    await asr.start();
    await asr.stop();

    expect(finalOutput).toBe('测试语音指令');
  });
});

分支 6:离散 PCM 音频识别与两阶段评测 (recognizeSamples)

无需开启麦克风或音频流,直接传入 16kHz 单声道 Float32Array PCM 音频数组进行识别。方法内部自动穿透 Pass 1 流式粗筛与 Pass 2 离线终审重打分(若已配置 Two-Pass),常用于音频回归评测、音频文件识别与服务端/离线批处理:

// samples 为 16000Hz、单声道、Float32Array PCM 采样数据
const result = await asr.recognizeSamples(pcmSamples);

console.log('定稿文本:', result.text);
console.log('产出阶段:', result.pass);              // 1: Pass 1 流式, 2: Pass 2 SenseVoice 终审
console.log('是否经过纠偏:', result.isRescored);       // boolean
console.log('流式粗筛文本:', result.rawStreamingText); // 第一阶段原始文本
if (result.fusionSource) {
  console.log('融合策略:', result.fusionSource);     // 'pass1' | 'pass2' | 'merged'
  console.log('融合原因:', result.fusionReason);     // 如 'pass2-empty', 'hotword-restored:...'
}

分支 7:独立使用端侧中文逆文本规整 (Chinese ITN)

SDK 导出了纯函数式的端侧零依赖中文 ITN 引擎,可直接用于汉字数字、序数词、百分比、温度及度量衡的规范化:

import { normalizeChineseNumbers, parseChineseInteger } from '@anov/sherpa-web';

// 1. 中文数字、序数、百分比、温度、度量衡规整
const text1 = normalizeChineseNumbers('把空调调到二十六度,风速调到第三档');
// => "把空调调到26度,风速调到第3档"

const text2 = normalizeChineseNumbers('电量还剩百分之八十五,预计还能行驶一百二十公里');
// => "电量还剩85%,预计还能行驶120公里"

// 2. 解析中文整数(支持 0 ~ 9999 规范中文数词或阿拉伯数字,非法返回 null)
const count = parseChineseInteger('一百二十五'); // => 125
const zero = parseChineseInteger('零');           // => 0

📋 配置契约与类型速查 (Reference)

OnlineRecognizerOptions 核心参数

| 配置项 | 类型 | 默认值 | 作用与约束 | | :--- | :--- | :---: | :--- | | hotwords | Array<string \| HotwordItem> | [] | 声学偏置热词列表(支持 word :score 语法) | | hotwordsScore | number | 2.0 | 热词全局基准声学加权得分 | | modelingUnit | 'cjkchar' \| 'bpe' \| 'cjkchar+bpe' | 'cjkchar' | 识别建模单元,中文指令建议选用 'cjkchar' | | profile | 'fast' \| 'balanced' \| 'accurate' | 'balanced' | 解码性能配置文件(balanced: maxActivePaths=16, blankPenalty=1.25) | | enableItn | boolean | true | 是否启用端侧通用中文逆文本规整 (ITN,汉字数字/度量衡转阿拉伯数字) | | twoPass | TwoPassOptions | undefined | 第二阶段 SenseVoice 离线重打分配置 | | audioSource | AudioSource | BrowserMicAudioSource | 音频输入源适配器,默认采集麦克风输入 | | vadDetector | VadDetector | 内部自动创建 | 负责驱动流切片的人声端点检测器 | | vadMode | 'hybrid' \| 'adaptive' \| 'silero' | 'hybrid' | VAD 运行模式(非 Fake 环境下默认为 'hybrid') | | enableVadGating | boolean | true | 是否启用 VAD 门禁切片与录音段分割 | | tailPaddingMs | number | 350 | 语音段尾部静音缓冲时长(毫秒),防止发音截断丢字 | | localBase | string | undefined | 静态资产根路径(如 '/') | | homophoneReplacer | HomophoneReplacerOptions | undefined | 应用层注入的业务别名/同音词纠偏表(单次正则替换,杜绝连锁污染) | | textPostProcessor | (text: string) => string | undefined | 自定义文本后处理管道函数 | | callbacks | SttCallbacks | {} | 流式中间结果、定稿文本及生命周期回调 |

OnlineRecognizer 核心公共方法

| 方法 | 返回类型 | 说明 | | :--- | :--- | :--- | | init() | Promise<void> | 初始化运行时环境,预热加载核心模型与 VAD | | start() | Promise<void> | 开启音频输入源并进入实时收音与识别流 | | stop() | Promise<void> | 停止收音,冲刷待裁决切片并注销输入音频源 | | setHotwords(hotwords, score?) | Promise<void> | 运行时动态热词更新(收音中自动排队至当前语音段后安全生效) | | recognizeSamples(samples) | Promise<RecognizeResult> | 离散 16kHz Float32Array PCM 音频直认(自动打通 Pass 1 与 Pass 2) | | getRecordedPcm() | Float32Array | 获取当前录音会话累积的 16kHz PCM 单声道音频数据 | | getRecordedWavBlob() | Blob | 获取当前录音会话编码生成的标准 WAV 格式 Blob | | dispose() | void | 彻底停止录音、销毁音频源、控制器、两阶段打分器与 Wasm 运行时 |

SttCallbacks 回调定义

interface SttCallbacks {
  onPartial?: (text: string) => void;
  onFinal?: (text: string, meta?: SttFinalMeta) => void;
  onSpeechStart?: () => void;
  onSpeechEnd?: (segment?: VadSegment) => void;
  onStatusChange?: (status: string) => void;
  onProgress?: (info: { loaded: number; total: number; percent: number }) => void;
  onLog?: (msg: string, level?: 'info' | 'ok' | 'warn' | 'err') => void;
  onPcmChunk?: (chunk: Float32Array) => void;
}

工具函数导出速查

import {
  normalizeChineseNumbers, // (text: string) => string: 中文数字/度量/温度通用 ITN 规整
  parseChineseInteger,      // (raw: string) => number | null: 解析 0~9999 汉字或阿拉伯整数
  stripPunctuation,         // (text: string) => string: 剔除文本标点符号
  computeConfidence,        // (ysProbs?: number[]) => number | undefined: 从 token 对数概率导出 [0, 1] 置信度
  serializeHotwords,        // 序列化热词列表为 Sherpa 格式
  extractPureHotwords,       // 提取热词纯词干
} from '@anov/sherpa-web';

⚠️ 开发注意细节 (Gotchas)

  1. 输入音频规格:底层模型严格只接收 16000Hz 采样率、单声道、32位浮点 PCM。使用自定义 AudioSource 时需经过重采样转换(可使用内置 WavResampler)。
  2. 解码算法限制:若 decoderOptions.decodingMethod 设为 'greedy_search',热词加权机制将不生效(需保持默认 'modified_beam_search')。
  3. 命名别名兼容:本库遵循统一领域语言规范,AsrEngine 与 OnlineRecognizer 互为等价导出别名。

📄 License

MIT © 2026 Anov Contributors