Metadata-Version: 2.4
Name: liteasr
Version: 2.2.0
Summary: LiteASR — unified speech-to-text library (production Gateway + SDK)
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: pydantic>=2
Requires-Dist: PyYAML>=6
Requires-Dist: httpx>=0.27
Requires-Dist: websockets>=12
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
Requires-Dist: respx>=0.21; extra == "dev"
Requires-Dist: fastapi>=0.110; extra == "dev"
Requires-Dist: python-multipart>=0.0.9; extra == "dev"
Requires-Dist: uvicorn[standard]>=0.27; extra == "dev"
Requires-Dist: opentelemetry-api>=1.27; extra == "dev"
Requires-Dist: opentelemetry-sdk>=1.27; extra == "dev"
Requires-Dist: ruff>=0.8; extra == "dev"
Requires-Dist: mypy>=1.13; extra == "dev"
Provides-Extra: local
Requires-Dist: faster-whisper>=1.0.0; extra == "local"
Provides-Extra: whisper-cpp
Provides-Extra: openai-whisper
Requires-Dist: openai-whisper>=20231117; extra == "openai-whisper"
Provides-Extra: funasr
Requires-Dist: funasr>=1.0.0; extra == "funasr"
Provides-Extra: vosk
Requires-Dist: vosk>=0.3.45; extra == "vosk"
Provides-Extra: whisperx
Requires-Dist: whisperx>=3.1.0; extra == "whisperx"
Provides-Extra: riva
Requires-Dist: nvidia-riva-client>=2.15.0; extra == "riva"
Provides-Extra: audio
Requires-Dist: ffmpeg-python>=0.2.0; extra == "audio"
Provides-Extra: vad
Requires-Dist: torch>=2.0.0; extra == "vad"
Provides-Extra: google
Requires-Dist: cryptography>=42; extra == "google"
Requires-Dist: google-cloud-speech>=2.26; extra == "google"
Provides-Extra: compat
Requires-Dist: fastapi>=0.110; extra == "compat"
Requires-Dist: uvicorn[standard]>=0.27; extra == "compat"
Requires-Dist: python-multipart>=0.0.9; extra == "compat"
Provides-Extra: proxy
Requires-Dist: fastapi>=0.110; extra == "proxy"
Requires-Dist: uvicorn[standard]>=0.27; extra == "proxy"
Requires-Dist: python-multipart>=0.0.9; extra == "proxy"
Requires-Dist: opentelemetry-api>=1.27; extra == "proxy"
Requires-Dist: opentelemetry-sdk>=1.27; extra == "proxy"

# LiteASR

**ASR 界的 LiteLLM** — 统一适配各家语音转文字模型（SDK + **可选** Proxy）。  
正式包版本 **2.2.0**（Realtime 深度 + 新厂覆盖 + 工程化门禁收官；近端 A **2.1.1** / 阶段 6 **2.1.0** 基线仍成立）。主业是换 Provider，不是会议/合规/评测产品。

**定位收敛已落地（2.0.0 breaking）：** 已删除 Duplex / PII / 假 DSP / `liteasr.eval` / Webhook 产品化 / ASS；字幕仅 json/text/srt/vtt；Proxy/VK 等保留可选。详见 [`docs/开发任务/定位收敛-适配层.md`](docs/开发任务/定位收敛-适配层.md)。企业管理面仍 **暂缓**。  
**客户端语言：** 仅 Python 一等 SDK + 既有 TypeScript 最小包；**不做 / 已取消** Go / Java / Kotlin / Swift 官方 SDK（其它语言走 OpenAPI codegen）。

Flat package at repo root (`liteasr/`). Mock 路径无需云密钥。

**运行产物勿提交：** 根目录 `*.db`（如 `usage.db`）、`liteasr-config/`、`liteasr-config-accept/` 等本地生成物已在 `.gitignore`；请用 `config/` 下的示例配置，勿把账本或私有密钥 YAML 推进仓库。

## 5 分钟 Quickstart（推荐）

见 **[`docs/quickstart-v1.5.md`](docs/quickstart-v1.5.md)**（Mock 无密钥）。Gateway / Docker 见 [`docs/quickstart-v1.0.md`](docs/quickstart-v1.0.md)。最短路径（Mock batch）：

```powershell
python -m venv .venv
.\.venv\Scripts\pip install -e ".[dev]"
.\.venv\Scripts\liteasr.exe transcribe tests/audio_fixtures/minimal.wav -m mock-batch
```

```bash
python -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/liteasr transcribe tests/audio_fixtures/minimal.wav -m mock-batch
```

Proxy 示例（进程内，无端口）：`python examples/proxy_fastapi/main.py`（需 `.[proxy]`）。  
Docker：[`docs/deploy-v1.0.md`](docs/deploy-v1.0.md)。  
**实时协议（Proxy `WS /v1/stream`，自研单轨）：** [`docs/realtime-ws-protocol.md`](docs/realtime-ws-protocol.md)。  
**会话中途 failover（默认关）：** [`docs/realtime-session-failover.md`](docs/realtime-session-failover.md)（`session.session_failover` / `LITEASR_SESSION_FAILOVER`）。  
**错误码手册：** [`docs/error-codes.md`](docs/error-codes.md)。  
**迁移指南（Deepgram / Whisper → LiteASR）：** [`docs/migration-deepgram-whisper.md`](docs/migration-deepgram-whisper.md)。  
更多示例：[`examples/README.md`](examples/README.md)。变更记录：[`CHANGELOG.md`](CHANGELOG.md)（当前 **2.2.0**）。  
能力矩阵：[`docs/capability-matrix.md`](docs/capability-matrix.md)。接入厂商：[`docs/providers/add-provider.md`](docs/providers/add-provider.md)。

v1.0 / v0.5 快速开始仍可用：[`docs/quickstart-v1.0.md`](docs/quickstart-v1.0.md) · [`docs/quickstart-v0.5.md`](docs/quickstart-v0.5.md)。

## 1 分钟复现（阶段 6 / v2.1 门禁 · Mock）

```powershell
# Windows (PowerShell)
python -m venv .venv
.\.venv\Scripts\pip install -e ".[dev]"
.\scripts\check_v2_1.ps1
# 门禁：版本/矩阵 + 新厂 Mock 抽测 + 全量 pytest + CLI Mock（无强制云密钥）
```

```bash
# POSIX
python -m venv .venv
.venv/bin/pip install -e ".[dev]"
bash scripts/check_v2_1.sh   # or: make check-v2.1
```

Expect: evidence subsets + full pytest green; CLI Mock transcript; **exit 0**.  
No cloud API keys required. Live vendor tests stay skipped unless `LITEASR_RUN_LIVE=1`.

Acceptance：[`docs/开发任务/阶段6-验收记录.md`](docs/开发任务/阶段6-验收记录.md)（**2.1.0**）。  
Realtime 深度门禁：`.\scripts\check_realtime_depth.ps1` / `bash scripts/check_realtime_depth.sh` / `make check-realtime-depth` → [`Realtime深度-验收记录.md`](docs/开发任务/Realtime深度-验收记录.md)（结论 **是**；无强制云密钥）。  
新厂覆盖门禁：`.\scripts\check_provider_coverage.ps1` / `bash scripts/check_provider_coverage.sh` / `make check-provider-coverage` → [`新厂覆盖-验收记录.md`](docs/开发任务/新厂覆盖-验收记录.md)（结论 **是**；无强制云密钥）。  
近端 A 档门禁：`.\scripts\check_vision_near.ps1` / `bash scripts/check_vision_near.sh` / `make check-vision-near` → [`最终愿景-近端验收记录.md`](docs/开发任务/最终愿景-近端验收记录.md)（**2.1.1**；无强制云密钥）。  
历史门禁仍可用：`scripts/check_v1_5.ps1` / `make check-v1.5`；`check_v1_0`；`check_v0_5`；`check_v0_1`。

## 下一步

**PR 前跑这个（日常工程门禁，非里程碑验收）：**

```powershell
.\scripts\check_eng.ps1
# 或: make check-eng
```

依次：scoped `ruff` → scoped `mypy` → `pytest -q` → OpenAPI schema。不取代 `check_v2_1` / `check_realtime_depth` / `check_provider_coverage`。  
推仓后 PR 看 GitHub **Checks / Actions**（`.github/workflows/ci.yml` 与本地 `check_eng` 同序；里程碑门禁见手动 `milestone` workflow）。

1. **发版 2.2.0 已成立（阶段 7A）：** 包 / OpenAPI / 文档对齐 **2.2.0**；变更见 [`CHANGELOG.md`](CHANGELOG.md) `[2.2.0]`；验收 [`发版-2.2.0-验收记录.md`](docs/开发任务/发版-2.2.0-验收记录.md)。**不上 PyPI**（阶段 7B 可选）。  
2. **新厂覆盖已验收（T901–T991）：** 门禁 `check_provider_coverage` 绿；结论 [`新厂覆盖-验收记录.md`](docs/开发任务/新厂覆盖-验收记录.md)；范围 [`provider-coverage-scope.md`](docs/provider-coverage-scope.md)；清单（归档）[`新厂覆盖-任务顺序清单.md`](docs/开发任务/新厂覆盖-任务顺序清单.md)。**本轮结束**（无下一项 T9xx）。**不做**多语言官方 SDK（Go / Java / Swift）。  
3. **Realtime 深度已验收（R0–R4 + T890/T891）：** 门禁 `check_realtime_depth` 绿；结论 [`Realtime深度-验收记录.md`](docs/开发任务/Realtime深度-验收记录.md)；范围 [`docs/realtime-depth-scope.md`](docs/realtime-depth-scope.md)；清单（归档）[`Realtime深度-任务顺序清单.md`](docs/开发任务/Realtime深度-任务顺序清单.md)。  
4. **近端 A 档已验收（T601–T607 / 包 2.1.1）：** 见 [`最终愿景-近端验收记录.md`](docs/开发任务/最终愿景-近端验收记录.md) / 门禁 `check_vision_near`。  
5. **已验收基线：** 阶段 6 / **2.1.0** — 门禁 `check_v2_1`；记录 [`阶段6-验收记录.md`](docs/开发任务/阶段6-验收记录.md)。  
6. **暂缓 / 可选：** 企业 Admin / OIDC / Helm；PyPI publish（7B）；**未**恢复 Duplex / PII / eval 等已删能力。

收敛验收（阶段 5 / 2.0.0）：[`docs/开发任务/定位收敛-验收记录.md`](docs/开发任务/定位收敛-验收记录.md)。  
定位决策：[`docs/开发任务/定位收敛-适配层.md`](docs/开发任务/定位收敛-适配层.md)。

## Install

```bash
pip install -e ".[dev]"
```

### Optional: local Whisper engine (T107 / scheme A)

No cloud API key. Install the optional extra (pulls `faster-whisper` /
CTranslate2 — **not** required by the core package):

```bash
pip install -e ".[local]"
# or: pip install -e ".[dev,local]"
```

Registry model: `local/whisper-tiny` (aliases `whisper-tiny`, `local-whisper-tiny`).

```bash
liteasr transcribe tests/audio_fixtures/minimal.wav -m whisper-tiny
```

If the extra is missing, the provider raises a clear `UnsupportedError` pointing
at `pip install 'liteasr[local]'`. Unit tests mock the engine and **do not**
download model weights by default.

### Optional: local whisper.cpp engine (T540)

Second local Whisper runtime **alongside** faster-whisper (does not replace it).
**Scheme locked: subprocess CLI** (not Python bindings; core never compiles
whisper.cpp). Install the optional extra marker, then provide a `whisper-cli`
binary and GGML weights:

```bash
pip install -e ".[whisper-cpp]"
# Build/install whisper.cpp CLI separately, then e.g.:
#   set WHISPER_CPP_BIN=C:\path\to\whisper-cli.exe
#   set WHISPER_CPP_MODEL=C:\path\to\ggml-tiny.bin
```

Registry model: `local/whisper-cpp-tiny` (aliases `whisper-cpp-tiny`,
`local-whisper-cpp-tiny`, `whisper-cpp`). Registry provider id is `local`
(prefix rule); engine package is `liteasr/providers/whisper_cpp/` and is
dispatched from the local provider for `whisper-cpp-*` models.

```bash
liteasr transcribe tests/audio_fixtures/minimal.wav -m whisper-cpp-tiny
```

If the binary / extra path is missing, the provider raises a clear
`UnsupportedError` pointing at `pip install 'liteasr[whisper-cpp]'`. Unit tests
inject a fake `runner=` and **do not** download GGML weights by default.
Optional live: `LITEASR_RUN_LIVE=1` (requires local `whisper-cli` + model).
Sidecar process pool → T547.

### Optional: local OpenAI Whisper (official Python) (T541)

Third local Whisper runtime **alongside** faster-whisper and whisper.cpp (does
**not** replace either). Optional extra pulls the official `openai-whisper`
package (imports as `whisper`; typically pulls **torch** — **not** required by
core or default CI):

```bash
pip install -e ".[openai-whisper]"
# or: pip install -e ".[dev,openai-whisper]"
```

Registry model: `local/openai-whisper-tiny` (aliases `openai-whisper-tiny`,
`local-openai-whisper-tiny`, `openai-whisper-local`). Distinct from cloud
`openai/whisper-1` (alias `openai-whisper` stays on the cloud API). Registry
provider id is `local` (prefix rule); engine package is
`liteasr/providers/openai_whisper/` and is dispatched from the local provider
for `openai-whisper-*` models.

```bash
liteasr transcribe tests/audio_fixtures/minimal.wav -m openai-whisper-tiny
```

If the extra is missing, the provider raises a clear `UnsupportedError` pointing
at `pip install 'liteasr[openai-whisper]'`. Unit tests inject a fake
`model_factory=` and **do not** download weights / import torch by default.
Optional live: `LITEASR_RUN_LIVE=1` (requires `liteasr[openai-whisper]`).
Sidecar → T547.

### Optional: local FunASR engine (T315)

Chinese local fallback **alongside** faster-whisper (does not replace it).
Registry: `funasr/paraformer-zh`. Install the optional extra (pulls `funasr` /
typically torch + ModelScope — **not** required by the core package or default
CI). From T542, SenseVoice **reuses** the same extra.

```bash
pip install -e ".[funasr]"
# or: pip install -e ".[dev,funasr]"
```

Registry model: `funasr/paraformer-zh` (aliases `paraformer-zh`, `funasr-zh`,
`funasr-paraformer`). `residency=CN`. No cloud API key.

```bash
liteasr transcribe tests/audio_fixtures/minimal.wav -m paraformer-zh
```

If the extra is missing, the provider raises a clear `UnsupportedError` pointing
at `pip install 'liteasr[funasr]'`. Unit tests inject a fake `AutoModel` and
**do not** download FunASR / ModelScope weights by default. Optional live:
`LITEASR_RUN_LIVE=1` (may download `paraformer-zh` once).

### Optional: local SenseVoice engine (T542)

Second Chinese local engine **alongside** FunASR (does **not** delete or
replace it). **Extra strategy locked: reuse `liteasr[funasr]`** (SenseVoice
loads via FunASR `AutoModel` + hub `iic/SenseVoiceSmall`; no separate
`sensevoice` extra).

```bash
pip install -e ".[funasr]"
```

Registry model: `sensevoice/sensevoice-small` (aliases `sensevoice-small`,
`sensevoice`, `sensevoice-zh`). Distinguishable from `funasr/paraformer-zh`.
`residency=CN`. No cloud API key.

```bash
liteasr transcribe tests/audio_fixtures/minimal.wav -m sensevoice-small
```

If the extra is missing, the provider raises a clear `UnsupportedError` pointing
at `pip install 'liteasr[funasr]'`. Unit tests inject a fake `model_factory=`
and **do not** download SenseVoice weights by default. Optional live:
`LITEASR_RUN_LIVE=1` (may download `SenseVoiceSmall` once).

### Optional: local Vosk engine (T543)

Lightweight offline Kaldi-based engine. Optional extra pulls PyPI `vosk`
(**not** required by core or default CI). **Model weights are never committed
to git** — download from [Alphacephei Vosk models](https://alphacephei.com/vosk/models)
and point LiteASR at the extracted directory:

```bash
pip install -e ".[vosk]"
# Download e.g. vosk-model-small-en-us-0.15.zip, extract, then:
#   set VOSK_MODEL=C:\path\to\vosk-model-small-en-us-0.15
# or place the folder under a parent and:
#   set VOSK_MODEL_DIR=C:\path\to\vosk-models
```

**Model path convention:**

| Env | Meaning |
|-----|---------|
| `VOSK_MODEL` | Path to one extracted Vosk **model directory** (`am/`, `conf/`, …) |
| `VOSK_MODEL_DIR` | Parent directory; LiteASR looks for `vosk-model-small-en` or common names like `vosk-model-small-en-us-0.15` |

Also searches `./models/<name>` and `./vosk-models/<name>` (both gitignored).

Registry model: `vosk/vosk-model-small-en` (aliases `vosk-model-small-en`,
`vosk-small-en`, `vosk`). No cloud API key.

```bash
liteasr transcribe tests/audio_fixtures/minimal.wav -m vosk
```

If the extra is missing, the provider raises a clear `UnsupportedError` pointing
at `pip install 'liteasr[vosk]'`. If the model directory is missing, raises
`UnsupportedError` with `reason=missing_vosk_model` and the same env hints.
Unit tests inject a fake `model_factory=` and **do not** download models by
default. Optional live: `LITEASR_RUN_LIVE=1` + `VOSK_MODEL` (or `_DIR`).
Riva → T545; sidecar → T547.

### Optional: local WhisperX engine (T544)

**Delivery path locked: independent Provider** (not `plugins` post-process).
WhisperX runs ASR + word-level alignment + optional pyannote diarization
in-process. Optional extra pulls PyPI `whisperx` (typically **torch** — **not**
required by core or default CI):

```bash
pip install -e ".[whisperx]"
```

Registry model: `whisperx/whisperx-tiny` (aliases `whisperx-tiny`, `whisperx`,
`local-whisperx`). No cloud API key for ASR/align. Diarization may need a
Hugging Face token:

| Env | Meaning |
|-----|---------|
| `WHISPERX_HF_TOKEN` | Preferred HF token for pyannote diarization |
| `HF_TOKEN` / `HUGGING_FACE_HUB_TOKEN` | Fallbacks |
| `WHISPERX_DEVICE` | Device (default `cpu`) |
| `WHISPERX_COMPUTE_TYPE` | Compute type (default `int8`) |

```bash
liteasr transcribe tests/audio_fixtures/minimal.wav -m whisperx
# word timestamps:
#   liteasr transcribe … -m whisperx  (model defaults timestamps=word)
```

Maps to unified A1: `result.words` when `timestamps=word`; `result.speakers` /
`Segment.speaker_id` / `Word.speaker_id` when `diarization=true`.

If the extra is missing, raises clear `UnsupportedError` pointing at
`pip install 'liteasr[whisperx]'`. Unit tests inject `model_factory=` and
**do not** download weights / import torch by default. Optional live:
`LITEASR_RUN_LIVE=1`. **Not** a meeting Duplex product; Riva → T545;
sidecar → T547.

### Optional: NVIDIA Riva ASR (T545)

**Transport locked: gRPC** (official `nvidia-riva-client` / `riva.client`
offline `Recognize`). **Not** HTTP; **not** NeMo training; **not**
OpenAI-compatible (→T546); **not** sidecar process management (→T547).

Optional extra pulls the Riva Python client (grpcio — **not** required by
core or default CI):

```bash
pip install -e ".[riva]"
```

Registry model: `riva/riva-asr` (aliases `riva-asr`, `riva`, `nvidia-riva`).
Point at a running Riva Speech server:

| Env | Meaning |
|-----|---------|
| `RIVA_URI` | Preferred gRPC target `host:port` (default `localhost:50051`) |
| `RIVA_SERVER` | Alias for `RIVA_URI` |

```bash
# set RIVA_URI=gpu-box:50051
liteasr transcribe tests/audio_fixtures/minimal.wav -m riva
```

If the extra is missing, raises clear `UnsupportedError` pointing at
`pip install 'liteasr[riva]'`. If the server is down / unreachable, raises
`ProviderDownError` with `reason=riva_unreachable` and URI hints.
Unit tests inject `client_factory=` and **do not** require a live Riva or
the optional extra. Optional live: `LITEASR_RUN_LIVE=1` + `RIVA_URI`.

### Optional: OpenAI-compatible transcription endpoint (T546)

**Selection locked: new** `providers/openai_compatible/` package (not an
extension of cloud `openai`). Generic multipart
`POST {api_base}/audio/transcriptions` for self-hosted / private Whisper
services — **no** new vendor class per endpoint. **Not** chat completions;
**not** multimodal audio→text (→T550). Sidecar process lifecycle for a local
compatible server is **T547** (`liteasr.runtime`). Coexists with cloud
`openai/whisper-1`.

```bash
# set env (auth_ref name only — never paste the key into models.yaml)
export OPENAI_COMPATIBLE_API_BASE=http://127.0.0.1:8080/v1   # POSIX
export OPENAI_COMPATIBLE_API_KEY=sk-...
# PowerShell:
#   $env:OPENAI_COMPATIBLE_API_BASE = "http://127.0.0.1:8080/v1"
#   $env:OPENAI_COMPATIBLE_API_KEY = "sk-..."

liteasr transcribe tests/audio_fixtures/minimal.wav -m openai-compatible
# aliases: openai-compatible-whisper | oai-compatible
# canonical: openai_compatible/whisper
```

| Item | Value |
|------|--------|
| Provider id | `openai_compatible` |
| Canonical model | `openai_compatible/whisper` |
| Env base | `OPENAI_COMPATIBLE_API_BASE` (alt: `OPENAI_API_BASE`) |
| Env key | `OPENAI_COMPATIBLE_API_KEY` |
| Vendor model | passthrough (default `whisper`; override `OPENAI_COMPATIBLE_MODEL`) |
| Scope | **Batch only** |

Missing `api_base` → clear `BadRequestError` (`reason=missing_api_base`).
Missing key → clear `AuthError` (`reason=missing_api_key`). Secrets never
appear in logs / error detail. Default tests use **respx** / httpx mock
(no live server). Optional live: `LITEASR_RUN_LIVE=1` + base/key.

### Optional: local sidecar process manager (T547)

**Selection locked: wired to T546** `openai_compatible` (not whisper.cpp CLI
pool / T540). Minimal lifecycle manager under `liteasr/runtime/`:
**spawn → health → concurrency queue → graceful stop**. Opt-in import —
**not** pulled by `import liteasr`; failures must never break the core SDK.
**Not** K8s / multi-host scheduling / Admin UI / multimodal (→T550+).

```python
from liteasr.runtime import SidecarConfig, SidecarManager
import sys

mgr = SidecarManager(
    SidecarConfig(
        command=[
            sys.executable, "-m", "liteasr.runtime.fake_sidecar",
            "--host", "127.0.0.1", "--port", "18080",
        ],  # CI fake; replace with a real OpenAI-compatible Whisper server
        api_base="http://127.0.0.1:18080/v1",
        max_concurrency=2,
    )
)
mgr.start()
with mgr.acquire():
    # set OPENAI_COMPATIBLE_API_BASE to mgr.api_base, then:
    # transcribe(..., model="openai_compatible/whisper")
    ...
mgr.stop()
```

| Item | Value |
|------|--------|
| Module | `liteasr.runtime` (`SidecarManager`) |
| Wired provider | `openai_compatible` (T546) |
| CI fake | `python -m liteasr.runtime.fake_sidecar` (stdlib HTTP; **no** GPU) |
| Concurrency | semaphore + queue; overflow → `RateLimitError` |
| Scope | process lifecycle only (transcription still via T546 Provider) |

See [`docs/sidecar-runtime.md`](docs/sidecar-runtime.md) and deploy notes.

### Optional: OpenAI GPT-4o transcribe (STT-only multimodal) (T550)

**Selection locked:** vendor model **`gpt-4o-transcribe`** on the official
OpenAI **`POST /v1/audio/transcriptions`** endpoint (same
`liteasr/providers/openai/` package as Whisper). **STT-only** audio→text —
**not** Chat Completions, **not** Realtime voice dialogue, **not** Vision.
No chat `messages` API is exposed on the integration surface. Coexists with
`openai/whisper-1` (same `OPENAI_API_KEY`).

```bash
# set OPENAI_API_KEY (auth_ref name only — never paste the key into models.yaml)
export OPENAI_API_KEY=sk-...   # POSIX
# PowerShell: $env:OPENAI_API_KEY = "sk-..."

liteasr transcribe tests/audio_fixtures/minimal.wav -m gpt-4o-transcribe
# aliases: openai-gpt-4o-transcribe | gpt4o-transcribe
# canonical: openai/gpt-4o-transcribe
```

| Item | Value |
|------|--------|
| Provider id | `openai` (shared with whisper-1) |
| Canonical model | `openai/gpt-4o-transcribe` |
| Vendor model | `gpt-4o-transcribe` |
| API | `POST /v1/audio/transcriptions` |
| Env key | `OPENAI_API_KEY` |
| Scope | **Batch STT-only** (non-chat) |

Default tests use **respx** mock (no live). Optional live:
`LITEASR_RUN_LIVE=1` + `OPENAI_API_KEY`.

### Optional: Google Gemini transcribe (STT-only multimodal) (T551)

**Selection locked:** vendor model **`gemini-3.5-transcribe`** via official
Gemini **`POST …/v1beta/models/{model}:generateContent`** with a single
audio **`inlineData`** part (WAV). Independent package
`liteasr/providers/gemini/`. **STT-only** audio→text — **not** multi-turn
chat, **not** tool/function calling, **not** images/vision. Distinct from
Google Cloud STT v2 (`google/long`). Coexists with `openai/whisper-1`.

```bash
# set GEMINI_API_KEY (auth_ref name only — never paste the key into models.yaml)
export GEMINI_API_KEY=AIza...   # POSIX
# PowerShell: $env:GEMINI_API_KEY = "AIza..."

liteasr transcribe tests/audio_fixtures/minimal.wav -m gemini-35-transcribe
# aliases: gemini-transcribe | google-gemini-transcribe
# canonical: gemini/gemini-3.5-transcribe
```

| Item | Value |
|------|--------|
| Provider id | `gemini` (distinct from `google` STT v2) |
| Canonical model | `gemini/gemini-3.5-transcribe` |
| Vendor model | `gemini-3.5-transcribe` |
| API | `POST …/v1beta/models/gemini-3.5-transcribe:generateContent` |
| Env key | `GEMINI_API_KEY` (`x-goog-api-key`) |
| Scope | **Batch STT-only** (non-chat) |

Default tests use **respx** mock (no live). Optional live:
`LITEASR_RUN_LIVE=1` + `GEMINI_API_KEY`. 通义 multimodal → T552.

### Optional: multi-format audio (T401–T402)

First-batch compressed inputs (**mp3 / opus / webm**) go through a **single**
pipeline transcoder — not per-vendor ffmpeg scripts. **Selection locked:
`ffmpeg-python`** via optional extra (plus system `ffmpeg` on `PATH`):

```bash
pip install -e ".[audio]"
# ensure `ffmpeg` is on PATH
```

Tiny fixtures: `tests/audio_fixtures/minimal.{mp3,opus,webm}`. Compatibility
cells: [`docs/兼容性矩阵.md`](docs/兼容性矩阵.md). Browser WebM /
MediaRecorder limits are documented in
[`docs/audio-formats.md`](docs/audio-formats.md) (not a full browser matrix).

### Optional: long-audio chunking (T405 / B1)

Default **off**. When enabled, audio over the duration/byte threshold is split
(silence-prefer cuts, hard-cut fallback), each slice uses the same Batch
`run_batch` / Mock path, and `text` / `segments` / `words` are stitched with
millisecond offsets. Failure default: whole request fails. See
[`docs/audio-chunking.md`](docs/audio-chunking.md).

```python
from liteasr import transcribe
from liteasr.audio import ChunkingConfig

transcribe(
    model="mock-batch",
    audio="long.wav",
    chunking=ChunkingConfig.enabled_defaults(),  # 60s threshold; fail_mode=all
)
```

If the extra is missing, the pipeline raises `AudioInvalidError`
(`detail.reason=missing_optional_extra`) pointing at
`pip install 'liteasr[audio]'`. Unit tests inject a fake transcoder and **do
not** require ffmpeg.

### Optional: local Silero VAD (T406 / C2)

**Selection locked: Silero.** Default **off** (v1.5 stream unchanged). Enable
with `stream(..., vad="silero")` or `session.local_vad="silero"`. Extra:
`liteasr[vad]` → `torch`. CI injects `set_vad_speech_fn` and never downloads
weights. Hook: after `FramePacker`, in `ManagedStreamSession.send_audio`,
before the Provider. **Not** a substitute for Deepgram cloud VAD /
endpointing. See [`docs/vad.md`](docs/vad.md).

### Optional: Groq Whisper batch (T214)

Third cloud Batch Provider — **selection locked: Groq Whisper** (not AssemblyAI).
OpenAI-compatible multipart transcriptions; no extra package beyond core `httpx`.

```bash
# set env (auth_ref name only — never paste the key into models.yaml / VK files)
export GROQ_API_KEY=gsk_...   # POSIX
# PowerShell: $env:GROQ_API_KEY = "gsk_..."

liteasr transcribe tests/audio_fixtures/minimal.wav -m groq/whisper-large-v3-turbo
# aliases: whisper-large-v3-turbo | groq-whisper
```

| Item | Value |
|------|--------|
| Provider id | `groq` |
| Canonical model | `groq/whisper-large-v3-turbo` |
| Env | `GROQ_API_KEY` |
| API base | `https://api.groq.com/openai/v1` |
| Scope | **Batch only** (no Groq Realtime / translations in v1.0 T214) |

Default tests use **respx** fixtures (no real key). Live smoke:
`LITEASR_RUN_LIVE=1` + `GROQ_API_KEY`. See [`CHANGELOG.md`](CHANGELOG.md) `[2.0.0]` / `[1.0.0]`.

### Optional: 阿里云 DashScope batch (T314)

Domestic CN Batch + Realtime + Omni STT-only — **selection locked: 阿里云 DashScope**
（非讯飞、非腾讯）. Batch ASR: multimodal-generation JSON + WAV Data-URL；Realtime:
raw Qwen-ASR WebSocket（**非**官方 SDK）；Omni STT-only（T552）:
compatible-mode chat/completions（**非**聊天助手产品）. No extra
package beyond core `httpx` / `websockets`. Registry `residency=CN`.

```bash
# set env (auth_ref name only — never paste the key into models.yaml / VK files)
export DASHSCOPE_API_KEY=sk-...   # POSIX
# PowerShell: $env:DASHSCOPE_API_KEY = "sk-..."

# Batch ASR-flash
liteasr transcribe tests/audio_fixtures/minimal.wav -m dashscope/qwen3-asr-flash
# aliases: qwen3-asr-flash | aliyun-asr | dashscope-asr

# Omni multimodal STT-only (T552; distinct API from ASR-flash)
liteasr transcribe tests/audio_fixtures/minimal.wav -m dashscope/qwen3-omni-flash
# aliases: qwen3-omni-flash | dashscope-omni | qwen-omni-flash

# Realtime (library): open_session on dashscope/qwen3-asr-flash-realtime
# aliases: qwen3-asr-flash-realtime | dashscope-realtime | dashscope-asr-stream
```

| Item | Value |
|------|--------|
| Provider id | `dashscope` |
| Canonical models | `dashscope/qwen3-asr-flash`（Batch）；`dashscope/qwen3-asr-flash-realtime`（Realtime T527）；`dashscope/qwen3-omni-flash`（Omni STT-only T552） |
| Env | `DASHSCOPE_API_KEY`（密钥不得入日志） |
| Batch ASR API | `https://dashscope.aliyuncs.com/api/v1`（multimodal-generation） |
| Omni STT API | `https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions` |
| Realtime WS | `wss://dashscope.aliyuncs.com/api-ws/v1/realtime?model=qwen3-asr-flash-realtime` |
| Transport | **HTTP** + **raw WebSocket**（`Bearer` + `OpenAI-Beta: realtime=v1`）— **not** SDK |
| Scope | Batch + Realtime + Omni STT-only（**非** Duplex/会议；**非**聊天助手产品） |

Default tests use **respx** / inject Fake WS (no real key). Live smoke:
`LITEASR_RUN_LIVE=1` + `DASHSCOPE_API_KEY`. See [`CHANGELOG.md`](CHANGELOG.md) `[2.1.0]` / `[1.5.0]`.

### Optional: Azure AI Speech (T403 + T511 Batch + T522 Realtime)

International Azure Speech — **selection locked** via **BaseHTTPProvider** (Batch)
and **raw Speech Service WebSocket** (Realtime; **not** official Speech SDK).
Three Registry models share `AZURE_SPEECH_KEY` / region:

| Model | Path | Host |
|-------|------|------|
| `azure/speech-to-text` | Short-audio sync REST (WAV body) | `*.stt.speech.microsoft.com` |
| `azure/batch-transcription` | Batch Transcription **Job** (contentUrls + poll) | `*.api.cognitive.microsoft.com` |
| `azure/speech-to-text-stream` | Realtime **WebSocket** (conversation recognition) | `wss://*.stt.speech.microsoft.com` |

**Not** the official Azure Speech SDK; **not** Duplex/meeting productization.
No optional Python extra beyond core `httpx` / `websockets`.

```bash
export AZURE_SPEECH_KEY=...          # POSIX
export AZURE_SPEECH_REGION=eastus    # optional; default eastus / models.yaml
# PowerShell: $env:AZURE_SPEECH_KEY = "..."; $env:AZURE_SPEECH_REGION = "eastus"

# Short-audio (local WAV / bytes via pipeline)
liteasr transcribe tests/audio_fixtures/minimal.wav -m azure/speech-to-text --language en-US
# aliases: azure-speech | azure-stt

# Batch Transcription Job (public HTTPS / blob SAS URL)
liteasr transcribe "https://example.com/long.wav" -m azure/batch-transcription --language en-US
# aliases: azure-batch | azure-batch-transcription | batch-transcription

# Realtime (library): open_session on azure/speech-to-text-stream — inject ws_connect in CI
# aliases: azure-speech-stream | azure-stt-stream | azure-realtime
```

| Item | Value |
|------|--------|
| Provider id | `azure` |
| Canonical models | `azure/speech-to-text`；`azure/batch-transcription`；`azure/speech-to-text-stream` |
| Env | `AZURE_SPEECH_KEY`（+ optional `AZURE_SPEECH_REGION`） |
| Short-audio API | `https://{region}.stt.speech.microsoft.com` |
| Job API | `https://{region}.api.cognitive.microsoft.com/speechtotext/v3.2/transcriptions` |
| Realtime WS | `wss://{region}.stt.speech.microsoft.com/speech/recognition/conversation/cognitiveservices/v1` |
| Transport | **HTTP** + **raw WebSocket** (`Ocp-Apim-Subscription-Key`) — **not** SDK |
| Scope | Batch + Realtime（**非** Duplex/会议） |

**Difference:** short-audio posts prepared WAV synchronously; Job submits
`contentUrls` (Azure fetches the URL), polls until Succeeded/Failed, then
downloads the transcription file. Job path needs a publicly reachable URL
(or pass `content_url`); local-only bytes should use `azure/speech-to-text`.
Realtime streams PCM frames over WebSocket (first frame prepends RIFF header).

Default tests use **respx** / inject Fake WS (no real key). Live smoke:
`LITEASR_RUN_LIVE=1` + `AZURE_SPEECH_KEY`（Job 另需可达 `AZURE_BATCH_CONTENT_URL`）。
Language/locale is required. See [`CHANGELOG.md`](CHANGELOG.md) `[2.1.0]` / `[2.0.0]`.

### Optional: Tencent Cloud ASR batch + realtime (T404 / T512 / T526)

Second domestic CN cloud alongside DashScope — **selection locked: 腾讯云 ASR**.
Batch models share `TENCENTCLOUD_SECRET_ID` / `TENCENTCLOUD_SECRET_KEY`
（`residency=CN`）via **in-tree TC3**. Realtime uses the **same SecretId/SecretKey**
plus `TENCENTCLOUD_APP_ID`, but signs the WebSocket URL with **HMAC-SHA1**
（**not** TC3 — see provider docs）.

| Model | Path |
|-------|------|
| `tencent/sentence-recognition` | 一句话识别 sync（WAV base64 + **TC3**） |
| `tencent/create-rec-task` | 录音文件识别 Job（`CreateRecTask` + poll；**TC3**） |
| `tencent/realtime` | 实时语音识别 WS（`wss://asr.cloud.tencent.com/asr/v2/<appid>`；**HMAC-SHA1**） |

**Not** official `tencentcloud-sdk-python`; **not** Duplex/meeting.
Does **not** replace DashScope / FunASR. **Not** Webhook/callback productization
(poll only for Job).

```bash
export TENCENTCLOUD_SECRET_ID=...    # POSIX (Batch + Realtime)
export TENCENTCLOUD_SECRET_KEY=...
export TENCENTCLOUD_APP_ID=...       # Realtime only
# PowerShell: $env:TENCENTCLOUD_SECRET_ID = "..."; $env:TENCENTCLOUD_SECRET_KEY = "..."
#             $env:TENCENTCLOUD_APP_ID = "..."

# 一句话（短音频本地 WAV）
liteasr transcribe tests/audio_fixtures/minimal.wav -m tencent/sentence-recognition --language zh
# aliases: tencent-asr | tencent-sentence

# 录音文件 Job（公网 URL / COS）
liteasr transcribe "https://example.com/long.wav" -m tencent/create-rec-task --language zh
# aliases: tencent-rec-task | tencent-recording | create-rec-task

# Realtime（PCM frames → partial/final/done）
# aliases: tencent-realtime | tencent-asr-stream | tencent-stream
```

| Item | Value |
|------|--------|
| Provider id | `tencent` |
| Canonical models | `tencent/sentence-recognition`；`tencent/create-rec-task`；`tencent/realtime` |
| Env (Batch) | `TENCENTCLOUD_SECRET_ID` + `TENCENTCLOUD_SECRET_KEY` |
| Env (Realtime) | + `TENCENTCLOUD_APP_ID`（HMAC-SHA1 URL 签名；**非** TC3） |
| API base (Batch) | `https://asr.tencentcloudapi.com` |
| API base (Realtime) | `wss://asr.cloud.tencent.com` |
| Transport | Batch：**HTTP + TC3**；Realtime：**WS + HMAC-SHA1**（自实现；**非**官方 SDK） |
| Residency | `CN` |
| Scope | Batch + Realtime（统一事件 `partial`/`final`/`done`） |

Default tests use **respx** / Fake WS inject (no real keys). Live smoke:
`LITEASR_RUN_LIVE=1` + SecretId/SecretKey（Job 可选 `TENCENT_REC_TASK_URL`；
Realtime 另需 `TENCENTCLOUD_APP_ID`）。
See [`CHANGELOG.md`](CHANGELOG.md) `[2.1.0]` / `[2.0.0]`.

### Optional: AssemblyAI batch + realtime (T501 / T523)

International long-tail — **selection locked: AssemblyAI**.

- **Batch** (`assemblyai/best`): official pre-recorded HTTP
  (`POST /v2/upload` → `POST /v2/transcript` → poll `GET /v2/transcript/{id}`).
- **Realtime** (`assemblyai/universal-streaming`, T523): Universal Streaming v3
  WebSocket (`wss://streaming.assemblyai.com/v3/ws`) — **not** the official SDK;
  **not** Duplex/meeting. Same raw-key auth (no `Bearer`).

```bash
export ASSEMBLYAI_API_KEY=...        # POSIX
# PowerShell: $env:ASSEMBLYAI_API_KEY = "..."

liteasr transcribe tests/audio_fixtures/minimal.wav -m assemblyai/best --language en
# aliases: assemblyai-best | aai-best
# Realtime (library): open_session on assemblyai/universal-streaming
# aliases: assemblyai-universal-streaming | assemblyai-realtime | aai-stream
```

| Item | Value |
|------|--------|
| Provider id | `assemblyai` |
| Canonical models | `assemblyai/best`（Batch）；`assemblyai/universal-streaming`（Realtime） |
| Env | `ASSEMBLYAI_API_KEY` |
| API base | Batch: `https://api.assemblyai.com`；Realtime: `wss://streaming.assemblyai.com` |
| Transport | Batch **HTTP**；Realtime **WebSocket**（raw v3；非 SDK） |
| Scope | Batch + Realtime（同钥；**非** Duplex/会议） |

Default tests use **respx** / Fake WS (no real key). Live smoke:
`LITEASR_RUN_LIVE=1` + `ASSEMBLYAI_API_KEY`. See [`CHANGELOG.md`](CHANGELOG.md)
`[2.1.0]`.

### Optional: Google Cloud STT v2 batch + streaming (T502 / T520)

International mainstream — **selection locked: Google Cloud Speech-to-Text
API v2**.

**Batch (T502):** synchronous `Recognize` (inline base64 `content`, recognizer `_`).
**Realtime (T520):** `StreamingRecognize` over **gRPC** (officially gRPC-only —
not REST / not WebSocket). Registry sibling `google/long-stream` coexists with
Batch `google/long`; vendor model stays `long`.

**Not** v1 as the primary path; **not** full Chirp SKU matrix; **not** multimodal
Gemini (→T551); **not** Duplex/meeting productization; **not** AWS/Azure.
Auth uses a service account (`GOOGLE_APPLICATION_CREDENTIALS` path or
`GOOGLE_SERVICE_ACCOUNT_JSON`); short-lived Bearer access tokens — **never** log
`private_key`. Live extras: `pip install 'liteasr[google]'` (cryptography +
`google-cloud-speech` for live streaming).

```bash
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/sa.json   # POSIX
# or: export GOOGLE_SERVICE_ACCOUNT_JSON='{"type":"service_account",...}'
# PowerShell: $env:GOOGLE_APPLICATION_CREDENTIALS = "..."

pip install -e ".[google]"   # live JWT mint + optional live gRPC

liteasr transcribe tests/audio_fixtures/minimal.wav -m google/long --language en
# aliases: google-long | google-stt | gcp-stt-v2

# Realtime (library): open_session on google/long-stream — inject stream_connect in CI
# aliases: google-long-stream | google-stt-stream | gcp-stt-v2-stream
```

| Item | Value |
|------|--------|
| Provider id | `google` |
| Canonical models | `google/long` (Batch); `google/long-stream` (Realtime) |
| Env | `GOOGLE_APPLICATION_CREDENTIALS` and/or `GOOGLE_SERVICE_ACCOUNT_JSON` (+ optional `GOOGLE_CLOUD_PROJECT`) |
| Batch transport | **HTTP** v2 Recognize |
| Realtime transport | **gRPC** v2 StreamingRecognize |
| Auth | Shared ADC / service account (T502) |

Default tests inject `access_token` + Fake gRPC / **respx** (no real credentials).
Live smoke: `LITEASR_RUN_LIVE=1` + ADC/JSON env + `liteasr[google]`. See
[`CHANGELOG.md`](CHANGELOG.md) `[2.1.0]`.

### Optional: AWS Transcribe batch + streaming (T503 / T521)

International mainstream — **selection locked: Amazon Transcribe**.

**Batch (T503):** `StartTranscriptionJob` + poll `GetTranscriptionJob` (WAV
staged via S3 `PutObject`). In-tree **SigV4**
(`liteasr.providers.transport.sigv4`) — **not** boto3/botocore.
**Realtime (T521):** WebSocket
`wss://transcribestreaming.{region}.amazonaws.com:8443/stream-transcription-websocket`
+ SigV4 query pre-sign (reuses T503 helper). Registry sibling
`aws/transcribe-stream` coexists with Batch `aws/transcribe`.

**Not** other AWS services as product surfaces; **not** Duplex/meeting
productization. Secrets stay in env; never log `AWS_SECRET_ACCESS_KEY`.

```bash
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
export AWS_REGION=us-east-1
export AWS_TRANSCRIBE_S3_BUCKET=your-bucket   # Batch only
# PowerShell: $env:AWS_ACCESS_KEY_ID = "..."; $env:AWS_SECRET_ACCESS_KEY = "..."

liteasr transcribe tests/audio_fixtures/minimal.wav -m aws/transcribe --language en
# aliases: aws-transcribe | amazon-transcribe | aws-stt

# Realtime (library): open_session on aws/transcribe-stream — inject ws_connect in CI
# aliases: aws-transcribe-stream | amazon-transcribe-stream | aws-stt-stream
```

| Item | Value |
|------|--------|
| Provider id | `aws` |
| Canonical models | `aws/transcribe` (Batch); `aws/transcribe-stream` (Realtime) |
| Env | `AWS_ACCESS_KEY_ID` + `AWS_SECRET_ACCESS_KEY` (+ optional `AWS_SESSION_TOKEN`); Batch also needs `AWS_TRANSCRIBE_S3_BUCKET` |
| Batch API | Transcribe JSON 1.1 + SigV4; media via S3 PutObject |
| Realtime API | WebSocket + SigV4 query pre-sign; event-stream AudioEvent |
| Scope | Batch + Realtime（同 provider；非其它 AWS 服务 / Duplex） |

Default tests use **SigV4 unit vectors** + **Fake WebSocket inject** / **respx**
(no live AWS). Live smoke: `LITEASR_RUN_LIVE=1` + keys (+ bucket for Batch).
See [`CHANGELOG.md`](CHANGELOG.md) `[2.1.0]`.

### Optional: Rev.ai batch (T504)

International long-tail Batch — **selection locked: Rev.ai** official async
Speech-to-Text job API (`POST /speechtotext/v1/jobs` multipart media → poll
`GET /jobs/{id}` → `GET /jobs/{id}/transcript`). **Not** Realtime/WS; **not**
Speechmatics / Gladia / Soniox (→T505–T507); **not** webhook productization
(poll only). Auth uses Bearer access token.

```bash
export REVAI_ACCESS_TOKEN=...        # POSIX
# PowerShell: $env:REVAI_ACCESS_TOKEN = "..."

liteasr transcribe tests/audio_fixtures/minimal.wav -m revai/machine --language en
# aliases: revai-machine | rev-ai | revai-asr
```

| Item | Value |
|------|--------|
| Provider id | `revai` |
| Canonical model | `revai/machine` |
| Env | `REVAI_ACCESS_TOKEN` |
| API base | `https://api.rev.ai/speechtotext/v1` |
| Transport | **HTTP** (`BaseHTTPProvider` + async job/poll/transcript) |
| Scope | **Batch only**（Realtime 未接；其它长尾 →T505–T507） |

Default tests use **respx** (no real key). Live smoke:
`LITEASR_RUN_LIVE=1` + `REVAI_ACCESS_TOKEN`. See [`CHANGELOG.md`](CHANGELOG.md)
`[2.1.0]`.

### Optional: Speechmatics batch (T505)

International long-tail Batch — **selection locked: Speechmatics** official
Batch Jobs API (`POST /v2/jobs` multipart `config`+`data_file` → poll
`GET /jobs/{id}` → `GET /jobs/{id}/transcript`). **Not** Realtime/WS; **not**
Gladia / Soniox (→T506–T507); **not** webhook productization (poll only).
Auth uses Bearer API key.

```bash
export SPEECHMATICS_API_KEY=...        # POSIX
# PowerShell: $env:SPEECHMATICS_API_KEY = "..."

liteasr transcribe tests/audio_fixtures/minimal.wav -m speechmatics/enhanced --language en
# aliases: speechmatics-enhanced | sm-enhanced | speechmatics-asr
```

| Item | Value |
|------|--------|
| Provider id | `speechmatics` |
| Canonical model | `speechmatics/enhanced` |
| Env | `SPEECHMATICS_API_KEY` |
| API base | `https://asr.api.speechmatics.com/v2` |
| Transport | **HTTP** (`BaseHTTPProvider` + async job/poll/transcript) |
| Scope | **Batch only**（Realtime 未接；其它长尾 →T506–T507） |

Default tests use **respx** (no real key). Live smoke:
`LITEASR_RUN_LIVE=1` + `SPEECHMATICS_API_KEY`. See [`CHANGELOG.md`](CHANGELOG.md)
`[2.1.0]`.

### Optional: Gladia batch (T506)

International long-tail Batch — **selection locked: Gladia** official
pre-recorded API (`POST /v2/upload` → `POST /v2/pre-recorded` → poll
`GET /v2/pre-recorded/{id}`). **Not** Realtime/WS; **not** Soniox (→T507);
**not** webhook / PII productization (poll only). Auth uses `x-gladia-key`.
Default model locked to `solaria-1`.

```bash
export GLADIA_API_KEY=...        # POSIX
# PowerShell: $env:GLADIA_API_KEY = "..."

liteasr transcribe tests/audio_fixtures/minimal.wav -m gladia/solaria-1 --language en
# aliases: gladia-solaria-1 | gladia-asr | solaria-1
```

| Item | Value |
|------|--------|
| Provider id | `gladia` |
| Canonical model | `gladia/solaria-1` |
| Env | `GLADIA_API_KEY` |
| API base | `https://api.gladia.io` |
| Transport | **HTTP** (`BaseHTTPProvider` + async upload/init/poll) |
| Scope | **Batch only**（Realtime 未接；Soniox →T507） |

Default tests use **respx** (no real key). Live smoke:
`LITEASR_RUN_LIVE=1` + `GLADIA_API_KEY`. See [`CHANGELOG.md`](CHANGELOG.md)
`[2.1.0]`.

### Optional: Soniox Batch + Realtime (T507 / T921)

International long-tail — **selection locked: Soniox**

- **Batch (T507):** official async API (`POST /v1/files` →
  `POST /v1/transcriptions` → poll → transcript). Auth: Bearer.
  Registry `soniox/stt-async-v5`.
- **Realtime (T921):** official WebSocket
  (`wss://stt-rt.soniox.com/transcribe-websocket`). Auth: same
  `SONIOX_API_KEY` as start-config `api_key`. Registry `soniox/stt-rt-v5`
  (vendor `stt-rt-v5`). Unified `partial`/`final`/`speech_end`/`done`.

**Not** Soniox SDK; **not** Duplex/meeting; **not** webhook / PII
productization. Realtime sibling on same package (T921).

```bash
export SONIOX_API_KEY=...        # POSIX
# PowerShell: $env:SONIOX_API_KEY = "..."

liteasr transcribe tests/audio_fixtures/minimal.wav -m soniox/stt-async-v5 --language en
# aliases: soniox-stt-async-v5 | soniox-asr | stt-async-v5

# Realtime (library): open_session on soniox/stt-rt-v5 — inject ws_connect in CI
# aliases: soniox-stt-rt-v5 | soniox-realtime | soniox-rt | stt-rt-v5
```

| Item | Value |
|------|--------|
| Provider id | `soniox` |
| Canonical models | `soniox/stt-async-v5`（Batch）；`soniox/stt-rt-v5`（Realtime） |
| Env | `SONIOX_API_KEY` |
| API base | Batch: `https://api.soniox.com`；Realtime: `wss://stt-rt.soniox.com` |
| Transport | Batch **HTTP**；Realtime **WebSocket**（raw；非 SDK） |
| Scope | Batch + Realtime（同钥；**非** Duplex/会议） |

Default tests use **respx** / FakeWebSocket (no real key). Live smoke:
`LITEASR_RUN_LIVE=1` + `SONIOX_API_KEY`. See [`CHANGELOG.md`](CHANGELOG.md).

### Optional: 讯飞开放平台 LFASR Batch + IAT Realtime (T530 / T525)

**Selection locked: `providers/xfyun/`**

- **Batch (T530):** 录音文件转写（标准版）LFASR v2
  (`POST /v2/api/upload` → poll `GET /v2/api/getResult`). Auth:
  `XFYUN_APP_ID` + `XFYUN_SECRET_KEY` (query `signa`).
- **Realtime (T525):** 语音听写（流式版）WebSocket
  (`wss://iat-api.xfyun.cn/v2/iat`). Auth: `XFYUN_APP_ID` + `XFYUN_API_KEY` +
  `XFYUN_API_SECRET` (HMAC-SHA256 query authorization). Unified
  `partial`/`final`/`done`.

Registry `residency=CN`. **Not** official SDK; **not** Duplex/meeting;
**not** Webhook/callback productization. Realtime sibling on same package (T525).

```bash
export XFYUN_APP_ID=...            # POSIX (shared)
# Batch:
export XFYUN_SECRET_KEY=...
# Realtime IAT:
export XFYUN_API_KEY=...
export XFYUN_API_SECRET=...
# PowerShell: $env:XFYUN_APP_ID = "..."; $env:XFYUN_SECRET_KEY = "..."
#             $env:XFYUN_API_KEY = "..."; $env:XFYUN_API_SECRET = "..."

liteasr transcribe tests/audio_fixtures/minimal.wav -m xfyun/lfasr --language zh
# aliases: xfyun-lfasr | xfyun-asr | iflytek-lfasr
# Realtime (library): open_session on xfyun/iat — inject ws_connect in CI
# aliases: xfyun-iat | xfyun-realtime | iflytek-iat
```

| Item | Value |
|------|--------|
| Provider id / 目录 | `xfyun`（写死；非 `iflytek/`） |
| Canonical models | `xfyun/lfasr`（Batch）；`xfyun/iat`（Realtime） |
| Env (Batch) | `XFYUN_APP_ID` + `XFYUN_SECRET_KEY` |
| Env (Realtime) | `XFYUN_APP_ID` + `XFYUN_API_KEY` + `XFYUN_API_SECRET` |
| API base | Batch: `https://raasr.xfyun.cn`；Realtime: `wss://iat-api.xfyun.cn` |
| Transport | Batch **HTTP**；Realtime **WebSocket**（raw IAT；非 SDK） |
| Scope | Batch + Realtime（**非** Duplex/会议） |

Default tests use **respx** / Fake WS (no real key). Live smoke:
`LITEASR_RUN_LIVE=1` + required env vars. See [`CHANGELOG.md`](CHANGELOG.md)
`[2.1.0]`.

### Optional: 火山引擎 / 豆包语音 AUC Batch (T531) + SAUC Realtime (T528)

**Batch selection locked: `providers/volcengine/` + 豆包语音 · 录音文件识别标准版（大模型 AUC）**

- API: `POST /api/v3/auc/bigmodel/submit` → poll `POST /api/v3/auc/bigmodel/query`
- Resource-Id: `volc.seedasr.auc`（豆包录音文件识别模型 2.0）
- Canonical: `volcengine/auc-bigmodel`

**Realtime selection locked: 豆包流式语音识别 · 大模型双向流式 SAUC**

- API: `wss://openspeech.bytedance.com/api/v3/sauc/bigmodel`
- Resource-Id: `volc.seedasr.sauc.duration`（流式模型 2.0 小时版）
- Canonical: `volcengine/sauc-bigmodel`
- Unified events `partial` / `final` / `done`

Auth（Batch + Realtime 共用）: `VOLCENGINE_API_KEY`（`X-Api-Key`）或 legacy
`VOLCENGINE_APP_KEY` + `VOLCENGINE_ACCESS_TOKEN`。Registry `residency=CN`。
**Not** official SDK; **not** Seeduplex Duplex dialogue; **not** Webhook 产品化.

```bash
export VOLCENGINE_API_KEY=...
# PowerShell: $env:VOLCENGINE_API_KEY = "..."

liteasr transcribe tests/audio_fixtures/minimal.wav -m volcengine/auc-bigmodel --language zh
# aliases: volcengine-auc | doubao-asr | volcengine-asr

# Realtime (stream / open_session):
# model volcengine/sauc-bigmodel
# aliases: volcengine-sauc | doubao-asr-stream | volcengine-realtime
```

| Item | Value |
|------|--------|
| Provider id / 目录 | `volcengine`（写死） |
| Batch model | `volcengine/auc-bigmodel` |
| Realtime model | `volcengine/sauc-bigmodel` |
| Env | `VOLCENGINE_API_KEY`（或 legacy APP_KEY + ACCESS_TOKEN） |
| Batch API base | `https://openspeech.bytedance.com` |
| Realtime WS | `wss://openspeech.bytedance.com/api/v3/sauc/bigmodel` |
| Transport | Batch HTTP submit+poll；Realtime 原始二进制 WS（非 SDK） |

Default tests use **respx** / Fake WS (no real key). Live smoke:
`LITEASR_RUN_LIVE=1` + `VOLCENGINE_API_KEY`. See [`CHANGELOG.md`](CHANGELOG.md)
`[2.1.0]`.

### Optional: 百度智能云短语音识别标准版 Batch (T532)

**Selection locked: `providers/baidu/` + 短语音识别标准版**

- API: `POST https://vop.baidu.com/server_api`（JSON pcm base64；同步）
- Canonical: `baidu/short-speech`
- Auth: `BAIDU_API_KEY` + `BAIDU_SECRET_KEY` → OAuth access_token
- Registry `residency=CN`
- **Not** 极速版 `pro_api`; **not** official SDK; Realtime **deferred**
  (not listed in stage-6 schedule); **not** Webhook 产品化.

```bash
export BAIDU_API_KEY=...
export BAIDU_SECRET_KEY=...
# PowerShell: $env:BAIDU_API_KEY = "..."; $env:BAIDU_SECRET_KEY = "..."

liteasr transcribe tests/audio_fixtures/minimal.wav -m baidu/short-speech --language zh
# aliases: baidu-asr | baidu-short-speech | baidu-vop
```

| Item | Value |
|------|--------|
| Provider id / 目录 | `baidu`（写死） |
| Batch model | `baidu/short-speech` |
| Env | `BAIDU_API_KEY` + `BAIDU_SECRET_KEY` |
| API base | `https://vop.baidu.com` |
| OAuth | `https://aip.baidubce.com/oauth/2.0/token` |
| Transport | **HTTP** 短语音同步（非 SDK） |

Default tests use **respx** (no real key). Live smoke:
`LITEASR_RUN_LIVE=1` + `BAIDU_API_KEY` + `BAIDU_SECRET_KEY`. See
[`CHANGELOG.md`](CHANGELOG.md) `[2.1.0]`.

### Optional: 华为云 SIS 一句话识别 Batch (T533)

**Selection locked: `providers/huawei/` + SIS 一句话识别（短语音）**

- API: `POST /v1/{project_id}/asr/short-audio`（JSON pcm16k16bit base64；同步）
- Canonical: `huawei/short-audio`
- Auth: `HUAWEI_ACCESS_KEY` + `HUAWEI_SECRET_KEY` + `HUAWEI_PROJECT_ID`
  → in-tree **SDK-HMAC-SHA256**（区域 `HUAWEI_REGION`，默认 `cn-north-4`）
- Registry `residency=CN`
- **Not** 录音文件识别 Job; **not** Realtime; **not** official SDK;
  **not** Webhook 产品化.

```bash
export HUAWEI_ACCESS_KEY=...
export HUAWEI_SECRET_KEY=...
export HUAWEI_PROJECT_ID=...
# export HUAWEI_REGION=cn-north-4
# PowerShell: $env:HUAWEI_ACCESS_KEY = "..."; ...

liteasr transcribe tests/audio_fixtures/minimal.wav -m huawei/short-audio --language zh
# aliases: huawei-asr | huawei-sis | huawei-short-audio
```

| Item | Value |
|------|--------|
| Provider id / 目录 | `huawei`（写死） |
| Batch model | `huawei/short-audio` |
| Env | `HUAWEI_ACCESS_KEY` + `HUAWEI_SECRET_KEY` + `HUAWEI_PROJECT_ID` |
| API base | `https://sis-ext.cn-north-4.myhuaweicloud.com`（按 region） |
| Auth | in-tree SDK-HMAC-SHA256（非官方 SDK） |
| Transport | **HTTP** 一句话识别同步 |

Default tests use **respx** (no real key). Live smoke:
`LITEASR_RUN_LIVE=1` + `HUAWEI_ACCESS_KEY` + `HUAWEI_SECRET_KEY` +
`HUAWEI_PROJECT_ID`. See [`CHANGELOG.md`](CHANGELOG.md) `[2.1.0]`.

### Optional: Deepgram Batch (T510)

Complements existing Deepgram Realtime (`deepgram/nova-2`). **Selection locked:
Deepgram** official pre-recorded REST (`POST /v1/listen` with WAV body, sync
JSON). Batch path lives in `liteasr/providers/deepgram/batch.py`, clearly
separated from `DeepgramStreamSession`. Reuses `DEEPGRAM_API_KEY`
(`Authorization: Token`). **Not** Realtime protocol changes; **not** Nova full
marketing matrix; **not** Azure Batch Job (→T511).

```bash
export DEEPGRAM_API_KEY=...        # POSIX
# PowerShell: $env:DEEPGRAM_API_KEY = "..."

liteasr transcribe tests/audio_fixtures/minimal.wav -m deepgram/nova-2-prerecorded --language en
# aliases: deepgram-nova-2-prerecorded | deepgram-batch | nova-2-prerecorded
```

| Item | Value |
|------|--------|
| Provider id | `deepgram`（与 Realtime 同 vendor） |
| Canonical model (Batch) | `deepgram/nova-2-prerecorded` |
| Canonical model (Realtime) | `deepgram/nova-2`（保留） |
| Env | `DEEPGRAM_API_KEY` |
| API base (Batch) | `https://api.deepgram.com` |
| Transport | **HTTP** pre-recorded listen（与 WS session 分离） |
| Scope | **Batch + Realtime** 并存于 Registry |

Default tests use **respx** (no real key). Live smoke:
`LITEASR_RUN_LIVE=1` + `DEEPGRAM_API_KEY`. See [`CHANGELOG.md`](CHANGELOG.md)
`[2.1.0]`.

### Optional: OpenAI-compatible HTTP (T110)

Drop-in Whisper-style `POST /v1/audio/transcriptions` (Facade only — same
Orchestrator / providers as the SDK). Core package does **not** require FastAPI:

```bash
pip install -e ".[compat]"
# alias: pip install -e ".[proxy]"

# run (pick one)
python -m liteasr.compat --host 127.0.0.1 --port 8000
uvicorn liteasr.compat.app:app --host 127.0.0.1 --port 8000
```

```bash
curl -s http://127.0.0.1:8000/v1/audio/transcriptions \
  -F file=@tests/audio_fixtures/minimal.wav \
  -F model=mock-batch
# → {"text":"..."}
```

`POST /v1/audio/translations` uses native `translation` capability (Mock
`mock-batch-translate` / OpenAI `whisper-1`); models without it return **501**.
See [`docs/compat.md`](docs/compat.md) (T316; no ASR+LLM cascade).

`response_format` (form or query): `json` (default) / `text` / `srt` / `vtt` /
`verbose_json`. Structured results are always produced internally; formats are
export-only. Subtitle formats without timestamps use a single cue `0 → duration`.

LiteASR-private fields (**not** OpenAI official): `provider` / `fallback`
(T215 routing); `diarization` / `hotwords` / `vad` (+ T302–T304 companions)
(T316). See [`docs/compat.md`](docs/compat.md).

**Drop-in matrix (T602):** field / five `response_format` / HTTP errors / intentional
diffs — [`docs/whisper-compat-matrix.md`](docs/whisper-compat-matrix.md)
(`tests/compat/test_whisper_dropin_matrix.py`, Mock + wav).

## CLI (transcribe one local wav)

Default model registry: `config/models.yaml` (override with `-c` / `--config`).

```bash
# print transcript text (exit 0 on success)
liteasr transcribe tests/audio_fixtures/minimal.wav -m mock-batch

# canonical model id + full Result JSON
liteasr transcribe tests/audio_fixtures/minimal.wav -m mock/batch-v1 --json

# custom registry path
liteasr transcribe ./a.wav -m mock-batch -c config/models.yaml

liteasr --help
liteasr transcribe --help
```

Expected errors (unknown model, bad/missing audio, etc.) exit non-zero.

## Library

```python
from liteasr import transcribe

result = transcribe(model="mock-batch", audio="tests/audio_fixtures/minimal.wav")
print(result.text)
```

Realtime (prefer context manager):

```python
import asyncio
from liteasr import stream

async def main():
    async with stream(model="mock-stream") as events:
        async for event in events:
            print(event.type, event.text)

asyncio.run(main())
```

## Docs

| Doc | Content |
|-----|---------|
| [`docs/quickstart-v1.5.md`](docs/quickstart-v1.5.md) | v1.5：Mock + 能力对齐 / 可选 Proxy |
| [`docs/开发任务/定位收敛-适配层.md`](docs/开发任务/定位收敛-适配层.md) | **定位收敛**：删留表与阶段计划 |
| [`docs/开发任务/新厂覆盖-任务顺序清单.md`](docs/开发任务/新厂覆盖-任务顺序清单.md) | **当前唯一**任务顺序清单（T9xx；**已验收** T991 门禁绿；本轮结束） |
| [`docs/开发任务/新厂覆盖-验收记录.md`](docs/开发任务/新厂覆盖-验收记录.md) | 新厂覆盖门禁结论 **是** — `check_provider_coverage` |
| [`docs/开发任务/Realtime深度-任务顺序清单.md`](docs/开发任务/Realtime深度-任务顺序清单.md) | Realtime 深度（归档）；**已验收**（T891 门禁绿） |
| [`docs/开发任务/Realtime深度-验收记录.md`](docs/开发任务/Realtime深度-验收记录.md) | Realtime 深度门禁结论 **是** — `check_realtime_depth` |
| [`docs/开发任务/阶段6-验收记录.md`](docs/开发任务/阶段6-验收记录.md) | 阶段 6 / **2.1.0** 验收成立 |
| [`docs/capability-matrix.md`](docs/capability-matrix.md) | M1：能力矩阵总表（对照 Registry） |
| [`docs/error-codes.md`](docs/error-codes.md) | M1/T603：公开稳定错误码手册 |
| [`docs/migration-deepgram-whisper.md`](docs/migration-deepgram-whisper.md) | M1/T604：Deepgram / Whisper 迁入 LiteASR |
| [`docs/providers/add-provider.md`](docs/providers/add-provider.md) | M1：新增 Provider 接入模板 |
| [`docs/quickstart-v1.0.md`](docs/quickstart-v1.0.md) | v1.0：SDK 旁路 + Docker/Proxy；Batch vs Stream vs Job |
| [`docs/openapi-ts-sdk.md`](docs/openapi-ts-sdk.md) | F2 / T319 / T605 / T610 / **T670**：OpenAPI（含 `/v1/stream` schemas）+ schema CI（`check_openapi_schema`）+ TypeScript HTTP + Realtime 客户端 |
| [`docs/collections/`](docs/collections/README.md) | M2/T612：Postman v2.1 + Insomnia v4 集合（models / transcriptions / transcribe；stream 说明） |
| [`docs/init-config.md`](docs/init-config.md) | M2/T613：`python -m liteasr.tools.init_config`（Mock models/VK/router） |
| [`packages/ts-sdk/`](packages/ts-sdk/) | `@liteasr/sdk` ≥0.2.0：`transcribe` + `models.list` + `realtime.connect` |
| [`docs/deploy-v1.0.md`](docs/deploy-v1.0.md) | v1.0 Docker / Compose（旁路 SDK vs Proxy） |
| [`docs/quickstart-v0.5.md`](docs/quickstart-v0.5.md) | v0.5 SDK 5-minute install + one command |
| [`CHANGELOG.md`](CHANGELOG.md) | Semver / deltas（当前 **2.2.0**） |
| [`examples/`](examples/) | Mock / Proxy FastAPI / OpenAI hypothetical / compat curl |
| [`examples/proxy_fastapi/`](examples/proxy_fastapi/) | FastAPI Proxy 集成（Mock + Demo VK） |
| [`docs/开发任务/v0.5-验收记录.md`](docs/开发任务/v0.5-验收记录.md) | v0.5 acceptance（可集成 SDK） |
| [`docs/开发任务/v0.1-验收记录.md`](docs/开发任务/v0.1-验收记录.md) | v0.1 acceptance |
| `tests/contract/` | Contract fixtures (Mock / OpenAI / Deepgram / local / **Groq**) |
| [`docs/测试/契约夹具与集成标记.md`](docs/测试/契约夹具与集成标记.md) | How to export goldens + `integration` marker |
| [`docs/兼容性矩阵.md`](docs/兼容性矩阵.md) | K3：wav/pcm/url × Provider（含 Groq） |

## Acceptance

- **发版 2.2.0（7A）**：[`docs/开发任务/发版-2.2.0-验收记录.md`](docs/开发任务/发版-2.2.0-验收记录.md) — `scripts/check_eng.ps1` / `.sh` / `make check-eng`（结论 **是**；不上 PyPI）
- **新厂覆盖**：[`docs/开发任务/新厂覆盖-验收记录.md`](docs/开发任务/新厂覆盖-验收记录.md) — `scripts/check_provider_coverage.ps1` / `.sh` / `make check-provider-coverage`（结论 **是**）
- **Realtime 深度**：[`docs/开发任务/Realtime深度-验收记录.md`](docs/开发任务/Realtime深度-验收记录.md) — `scripts/check_realtime_depth.ps1` / `.sh` / `make check-realtime-depth`（结论 **是**）
- **近端 A / 2.1.1**：[`docs/开发任务/最终愿景-近端验收记录.md`](docs/开发任务/最终愿景-近端验收记录.md) — `scripts/check_vision_near.ps1` / `.sh` / `make check-vision-near`
- **阶段 6 / 2.1.0**：[`docs/开发任务/阶段6-验收记录.md`](docs/开发任务/阶段6-验收记录.md) — `scripts/check_v2_1.ps1` / `.sh` / `make check-v2.1`
- **v1.5（历史）**：[`docs/开发任务/v1.5-验收记录.md`](docs/开发任务/v1.5-验收记录.md) — `scripts/check_v1_5.ps1` / `.sh`
- **v0.5**：[`docs/开发任务/v0.5-验收记录.md`](docs/开发任务/v0.5-验收记录.md) — `scripts/check_v0_5.ps1` / `.sh`
- **v0.1**：[`docs/开发任务/v0.1-验收记录.md`](docs/开发任务/v0.1-验收记录.md) — `scripts/check_v0_1.ps1` / `.sh`
