Metadata-Version: 2.4
Name: soulshell-kws
Version: 0.2.4
Summary: 本地离线唤醒词检测组件，支持中英文及混合关键词，支持 Sherpa-ONNX 和 Vosk 引擎
Project-URL: Repository, https://gitee.com/soulshellcn/soulshell-kws
Project-URL: Changelog, https://gitee.com/soulshellcn/soulshell-kws/blob/dev/Changelog.md
Author-email: SoulShell Team <dev@soulshell.cn>
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.10
Requires-Dist: numpy>=1.21.0
Provides-Extra: all
Requires-Dist: allosaurus>=1.0.2; extra == 'all'
Requires-Dist: g2p-arpabet>=2.3.0; extra == 'all'
Requires-Dist: g2p-en>=2.1.0; extra == 'all'
Requires-Dist: onnxruntime>=1.18.0; extra == 'all'
Requires-Dist: pyaudio>=0.2.14; extra == 'all'
Requires-Dist: pypinyin>=0.50.0; extra == 'all'
Requires-Dist: sherpa-onnx>=1.12.23; extra == 'all'
Requires-Dist: vosk>=0.3.45; extra == 'all'
Provides-Extra: dev
Requires-Dist: basedpyright>=1.37.1; extra == 'dev'
Requires-Dist: pre-commit>=4.3.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.1.0; extra == 'dev'
Requires-Dist: pytest>=8.0.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Provides-Extra: mic
Requires-Dist: pyaudio>=0.2.14; extra == 'mic'
Provides-Extra: sherpa
Requires-Dist: g2p-en>=2.1.0; extra == 'sherpa'
Requires-Dist: onnxruntime>=1.18.0; extra == 'sherpa'
Requires-Dist: pypinyin>=0.50.0; extra == 'sherpa'
Requires-Dist: sherpa-onnx>=1.12.23; extra == 'sherpa'
Provides-Extra: sherpa-full
Requires-Dist: allosaurus>=1.0.2; extra == 'sherpa-full'
Requires-Dist: g2p-arpabet>=2.3.0; extra == 'sherpa-full'
Requires-Dist: g2p-en>=2.1.0; extra == 'sherpa-full'
Requires-Dist: onnxruntime>=1.18.0; extra == 'sherpa-full'
Requires-Dist: pypinyin>=0.50.0; extra == 'sherpa-full'
Requires-Dist: sherpa-onnx>=1.12.23; extra == 'sherpa-full'
Provides-Extra: vosk
Requires-Dist: pypinyin>=0.50.0; extra == 'vosk'
Requires-Dist: vosk>=0.3.45; extra == 'vosk'
Description-Content-Type: text/markdown

# SoulShell-KWS

本地离线唤醒词检测组件，支持中英文及混合关键词，默认在 CPU 上运行。

## 功能特性

- 本地离线运行，无需联网
- 支持自定义唤醒词（中文、英文、中英混合）
- 低延迟实时检测
- 支持多个唤醒词同时监听
- 支持关键词二次唤醒
- **多引擎支持**：Sherpa-ONNX（端到端 KWS）和 Vosk（ASR + 拼音匹配）
- 统一的引擎接口与工厂模式
- 完整的日志与异常处理

## 支持的引擎

| 引擎 | 特点 | 适用场景 |
|------|------|----------|
| **sherpa-onnx（推荐）** | 端到端 KWS 模型，低延迟，高精度 | 推荐用于生产环境 |
| **vosk** | ASR + 拼音/文本匹配，灵活配置 | 需要自定义匹配逻辑时使用 |

## 技术栈

- **Python**: 3.10+
- **包管理**: uv
- **异步框架**: asyncio
- **数据验证**: dataclasses
- **日志**: logging (标准库)

## 项目结构

```
soulshell-kws/
├── src/soulshell_kws/
│   ├── __main__.py           # python -m 入口
│   ├── cli.py                # CLI 实现
│   ├── base.py               # 抽象基类与结果结构
│   ├── factory.py            # 引擎工厂
│   └── engines/
│       ├── sherpa_engine.py  # Sherpa-ONNX 引擎实现
│       └── vosk_engine.py    # Vosk 引擎实现
├── examples/
│   └── sherpa/
│       └── sherpa_usage.py   # API 调用示例
├── pyproject.toml
├── Changelog.md
└── README.md
```

## 快速开始

### 1. 安装

```bash
# 安装基础包
pip install soulshell-kws

# 推荐：安装 Sherpa-ONNX 引擎
pip install "soulshell-kws[sherpa]"

# 安装 Vosk 引擎
pip install "soulshell-kws[vosk]"

# 如需麦克风支持
pip install "soulshell-kws[mic]"

# 安装所有运行时能力
pip install "soulshell-kws[all]"
```

### 2. 下载模型

**Sherpa-ONNX 模型**（推荐，中英双语）：

官方模型列表见 [Sherpa-ONNX KWS 预训练模型](https://k2-fsa.github.io/sherpa/onnx/kws/pretrained_models/index.html)。

```bash
mkdir -p models
curl -L -o sherpa-onnx-kws-zipformer-zh-en-3M-2025-12-20.tar.bz2 \
  https://github.com/k2-fsa/sherpa-onnx/releases/download/kws-models/sherpa-onnx-kws-zipformer-zh-en-3M-2025-12-20.tar.bz2
tar -xjf sherpa-onnx-kws-zipformer-zh-en-3M-2025-12-20.tar.bz2 -C models/
```

**Vosk 模型**：

```bash
# 中文模型
curl -L -O https://alphacephei.com/vosk/models/vosk-model-small-cn-0.22.zip
unzip vosk-model-small-cn-0.22.zip -d models/

# 英文模型
curl -L -O https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip
unzip vosk-model-small-en-us-0.15.zip -d models/
```

### 3. API 使用

#### Sherpa-ONNX 引擎（推荐）

```python
import asyncio
from soulshell_kws.factory import WakeWordEngineFactory

async def main():
    engine = await WakeWordEngineFactory.create_engine(
        engine_name="sherpa-onnx",
        config={
            "tokens": "models/sherpa-onnx-kws-zipformer-zh-en-3M-2025-12-20/tokens.txt",
            "encoder": "models/sherpa-onnx-kws-zipformer-zh-en-3M-2025-12-20/encoder-epoch-13-avg-2-chunk-8-left-64.onnx",
            "decoder": "models/sherpa-onnx-kws-zipformer-zh-en-3M-2025-12-20/decoder-epoch-13-avg-2-chunk-8-left-64.onnx",
            "joiner": "models/sherpa-onnx-kws-zipformer-zh-en-3M-2025-12-20/joiner-epoch-13-avg-2-chunk-8-left-64.onnx",
            "wake_words": ["你好 世界", "HELLO WORLD"],
            "sample_rate": 16000,
            "auto_generate_keywords":false,
            "two_stage_wake": {
                "enabled": true,
                "prefix_labels": ["你好", "HELLO"],
                "target_labels": ["世界", "WORLD"],
                "full_labels": ["你好 世界", "HELLO WORLD"],

                "window_seconds": 0.35,
                "latency_grace": 0.8,
                "latency_max": 5.0,
                "cooldown_seconds": 0.3,

                "ema_alpha": 0.2,
                "latency_margin": 0.4
            }
        },
    )

    audio_chunk = b"..."  # 16-bit PCM, 16kHz
    result = await engine.process_audio(audio_chunk)
    # result = await engine.process_audio_two_stage_wake(audio_chunk)#二次唤醒，two_stage_wake.enabled为false走process_audio
    if result.detected:
        print(f"检测到: {result.keyword}")

    await engine.unload()

asyncio.run(main())
```

#### Vosk 引擎

```python
import asyncio
from soulshell_kws.factory import WakeWordEngineFactory

async def main():
    # 中文（使用拼音匹配）
    engine = await WakeWordEngineFactory.create_engine(
        engine_name="vosk",
        config={
            "model_path": "models/vosk-model-small-cn-0.22",
            "wake_words": ["你好"],
            "use_pinyin_match": True,
            "sample_rate": 16000,
        },
    )

    audio_chunk = b"..."  # 16-bit PCM, 16kHz
    result = await engine.process_audio(audio_chunk)
    if result.detected:
        print(f"检测到: {result.keyword}")

    await engine.unload()

asyncio.run(main())
```

```python
async def english_example():
    # 英文（使用文本匹配）
    engine = await WakeWordEngineFactory.create_engine(
        engine_name="vosk",
        config={
            "model_path": "models/vosk-model-small-en-us-0.15",
            "wake_words": ["你好 世界", "hello world"],
            "use_pinyin_match": False,
            "sample_rate": 16000,
        },
    )
    # ...
```

### 4. 配置参数

#### Sherpa-ONNX 配置

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `tokens` | str | ✅ | tokens.txt 文件路径 |
| `encoder` | str | ✅ | encoder.onnx 文件路径 |
| `decoder` | str | ✅ | decoder.onnx 文件路径 |
| `joiner` | str | ✅ | joiner.onnx 文件路径 |
| `wake_words` | list[str] | ✅ | 唤醒词列表 |
| `sample_rate` | int | ❌ | 采样率，默认 16000 |
| `keywords_score` | float | ❌ | 关键词分数，默认 2.5 |
| `keywords_threshold` | float | ❌ | 检测阈值，默认 0.08 |
| `num_threads` | int | ❌ | 线程数，默认 4 |
| `provider` | str | ❌ | 推理后端，默认 "cpu" |

#### Vosk 配置

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `model_path` | str | ✅ | Vosk 模型目录路径 |
| `wake_words` | list[str] | ✅ | 唤醒词列表 |
| `use_pinyin_match` | bool | ❌ | 是否使用拼音匹配（中文 True，英文 False），默认 True |
| `sample_rate` | int | ❌ | 采样率，默认 16000 |

### 5. 返回结果

`WakeWordResult` 数据结构：

| 字段 | 类型 | 说明 |
|------|------|------|
| `detected` | bool | 是否检测到唤醒词 |
| `keyword` | str \| None | 检测到的唤醒词 |
| `confidence` | float | 置信度 (0.0-1.0) |
| `timestamp` | float | 检测时间戳 |
| `audio_duration` | float | 音频时长（秒） |
| `engine_name` | str | 引擎名称 |

## CLI 使用

```bash
# 查看帮助
soulshell-kws --help

# 列出可用引擎
soulshell-kws list-engines

# 查看引擎信息
soulshell-kws engine-info --engine sherpa-onnx
soulshell-kws engine-info --engine vosk

# 列出音频设备
soulshell-kws list-devices

# 麦克风检测
soulshell-kws detect-mic --config config.json --device 1

# 二次唤醒
soulshell-kws detect-mic_two_stage --config config.json --engine sherpa-onnx --debug --enabled

# 文本匹配测试
soulshell-kws match-text --text "你好 世界" --config config.json

# 设置唤醒词
soulshell-kws set-wake-words --config config.json --wake-words "你好 世界" "HELLO WORLD"

# 批量测试
soulshell-kws batch-test --config config.json --audio-dir output_wakewords
```

## 在其他项目中使用

### 安装

```bash
# 从 PyPI 安装
pip install soulshell-kws[sherpa]  # Sherpa-ONNX 引擎
pip install soulshell-kws[vosk]    # Vosk 引擎
pip install soulshell-kws[all]     # 所有引擎

# 从源码构建
uv build
pip install dist/soulshell_kws-*.whl
```

### 作为依赖添加

```toml
# pyproject.toml
dependencies = [
    "soulshell-kws[sherpa]",  # 或 [vosk], [all]
]
```

## 开发

```bash
# 安装开发依赖
uv sync --extra dev

# 运行测试
uv run pytest

# 代码检查
uv run ruff check src/
uv run basedpyright src/

# 格式化
uv run ruff format src/
```

## 依赖

### 核心依赖
- Python 3.10+
- numpy >= 1.21.0

### Sherpa-ONNX 引擎
- sherpa-onnx >= 1.12.23
- onnxruntime >= 1.18.0
- g2p-en >= 2.1.0

### Vosk 引擎
- vosk >= 0.3.45
- pypinyin >= 0.50.0

### 可选
- pyaudio >= 0.2.14（麦克风支持）

## 参考

- [Sherpa-ONNX](https://github.com/k2-fsa/sherpa-onnx)
- [Vosk](https://alphacephei.com/vosk/)
- [pypinyin](https://github.com/mozillazg/python-pinyin)

## License

MIT
