The open standard interface between apps and speech-to-text engines
Project description
Standard ASR
The open standard interface between applications and speech-recognition engines. Apps integrate speech-to-text once and gain every engine. Engines implement once and reach every app.
[!WARNING] Standard ASR is pre-release and a work in progress — breaking changes may land at any time. For production use, wait for the
v1.0.0release, where we stabilize the public API and enforce a migration policy for breaking changes. We strictly follow semantic versioning. Try it out and tell us what you think — let's shape the future of ASR tooling together.
The problem
Speech recognition never got its standard interface. Every ASR library and cloud API ships its own calling convention, its own audio-input rules, its own streaming protocol. Integrating one engine means writing an adapter; integrating five means maintaining five. So in practice most applications hard-wire two or three engines — and their users are stuck with whatever languages and domains those engines happen to be good at, waiting for an "official support" release that usually never comes. Meanwhile the model that would actually serve them best already exists.
Standard ASR removes that tax: one vendor-neutral interface that both sides implement. Applications code against the protocol and gain every compliant engine, cloud API or local model. Engines implement it once and reach every application. Switching engines becomes a one-line model-key change — not another adapter.
"Nice idea — but how does a protocol with no adopters get adopted?"
That's the right question to ask, so let's answer it up front.
Standard ASR does not need any vendor's cooperation to be useful today. For existing engines, compliance is a thin adapter — not a rewrite — and adapters are ordinary pip-installable plugin packages that anyone can publish. An application developer gets the payoff — one interface, swappable engines — from day one, with zero engines "officially" on board. If the protocol earns an ecosystem, engine authors gain an organic incentive to ship native compliance: one interface implemented means every Standard ASR application is a potential user, plus a CLI, an HTTP/WebSocket server, and a compliance test suite for free. But nothing waits on that flywheel to start turning.
"Why a protocol and plugins, and not another all-in-one package?" Because the all-in-one shape has been tried, repeatedly, and it structurally fails: a monolith that bundles adapters for every engine becomes a maintenance bottleneck (new models outpace any single team), a dependency minefield (engines pin conflicting numpy/torch versions in one process), and a licensing trap (GPL/AGPL engines can't be bundled with permissive ones). Model creators won't open pull requests against someone else's mega-repo. Standard ASR inverts the structure: the core defines the protocol and toolchain; every engine lives in its own independently-maintained, independently-licensed package. Maintenance stays with the people who know each engine best, and the core never becomes the bottleneck.
Why build on Standard ASR?
- Write once, run with any engine. Code against the protocol, not the vendor. Switching from a cloud API to a local model (or the reverse) is a one-line model-key change — your integration work survives every vendor decision you'll make later.
- One streaming model for every engine. Real-time ASR is the wild west: some engines
rewrite their interim results, some never revise a token, some merge already-emitted
segments after a second decoding pass. Standard ASR unifies all of it under one event
protocol with explicit stability guarantees — designed against an in-repo survey of 30+
real engine APIs (
docs/research/). - Audio negotiation, batteries included. Hand over what you have — a file path, raw bytes, a NumPy array, a URL — and the framework negotiates and converts to whatever form the engine accepts, loudly reporting anything lossy. No more sample-rate guesswork.
- No dependency hell, no licensing traps. Each engine is an isolated, pip-installable plugin, so conflicting dependencies and restrictive licenses stay contained in the packages that carry them.
- The choice goes to the user. End users — especially for under-served languages and domains — install the engine that serves them best and use it immediately, without waiting for the app author to add support.
Quickstart
Install Standard ASR and a compliant engine plugin, then discover and transcribe:
# Install (see Installation below for extras)
pip install standard-asr
# uv: uv add standard-asr
# Install a compliant engine plugin. Each engine is its own package; experimental
# plugins install from their repo until they publish to PyPI.
pip install "std-faster-whisper @ git+https://github.com/standard-voice/std-faster-whisper.git"
standard-asr list # discover installed engines
standard-asr compliance entrypoints # verify the plugins resolve correctly
Python usage
Transcribe
Discover whatever compliant engines are installed, then transcribe:
from standard_asr import discover_models
registry = discover_models()
engine = registry.create("faster-whisper/large-v3") # any installed engine's model key
# Pass the audio you already have — a file path, raw bytes, a base64 data URI, or a
# NumPy array. Standard ASR negotiates the right form for the chosen engine and converts
# only when needed (every lossy step is reported as a structured diagnostic).
result = engine.transcribe("meeting.wav")
print(result.text)
The same app code runs against any other compliant engine — only the model key changes.
Results always have the same shape — no format flags that turn the return value into a string, no fields that appear and disappear. Render subtitles from any engine's result:
from standard_asr import to_srt, to_vtt
print(to_srt(result)) # works for every compliant engine
Discover capabilities & configuration
Engines differ — that's the point. Instead of guessing, ask:
engine.supports("batch.word_timestamps") # True / False, fail-closed
engine.supports("streaming.guidance.phrase_hints")
engine.supports("streaming_input") # can it consume live audio?
registry.config_schema("faster-whisper/large-v3") # the engine's init-config JSON Schema —
# render a settings UI without
# instantiating (secrets are marked)
Unsupported parameters never degrade silently: depending on policy, they either raise
(strict) or are dropped with a structured diagnostic telling you exactly what was
ignored and why (best_effort).
Stream
Full-duplex streaming — feed audio while receiving live results. Requires a streaming-capable engine:
# Ask the engine for the PCM wire format it wants (sample rate + encoding), and
# encode your microphone frames to match. A correctly-declared streaming engine
# returns a concrete format; build an `AudioFormat` yourself if you need a specific one.
audio_format = engine.recommended_wire_format()
async with engine.start_transcription(audio_format=audio_format) as session:
session.feed(microphone()) # any (async) iterable of PCM byte chunks
segments: dict[str, str] = {}
async for event in session:
if event.type in ("partial", "final"):
segments[event.segment_id] = event.text # partial: may change; final: settled
elif event.type == "supersede":
for old_id in event.old_ids: # engine re-segmented (e.g. two-pass
del segments[old_id] # rescoring); replacements follow
render(segments)
print(session.result().text) # collapse the session into a TranscriptionResult
Those three branches are the complete core reduce — handle them and your app is safe on
every compliant engine, including ones that rewrite interim text or merge segments after the
fact. Engines that never do these things simply never emit those events. Voice agents can go
further and act on event.stable_until, the engine's guarantee of how much of the text is
frozen and will never change.
Not async?
SyncSessionwraps any streaming session behind a blocking iterator. Seedocs/spec/for the full streaming contract — segment lifecycle, stability guarantees, reconnect semantics, and backpressure rules.
Who benefits?
| You are… | You get… |
|---|---|
| An application developer | One integration that works with every compliant engine; zero vendor lock-in; automatic discovery of whatever the user installs. |
| An ASR engine developer / researcher | Focus on the model, not boilerplate. Implement one interface and get a CLI, a Web API server, and a compliance test suite for free. Reach the whole ecosystem instantly. |
| An end user | Access to cutting-edge models sooner, and the freedom to pick the engine that fits your language or domain — not whatever the app author happened to choose. |
CLI
standard-asr list # what's installed?
standard-asr show faster-whisper/large-v3 # properties & capabilities
standard-asr transcribe faster-whisper/large-v3 audio.wav # quick transcription
standard-asr serve # expose engines over HTTP/WS
standard-asr doctor # diagnose plugin dependency conflicts
Installation
# pip
pip install standard-asr
# uv
uv pip install standard-asr
# or, in a uv project:
uv add standard-asr
With extras:
# pip
pip install "standard-asr[audio]"
pip install "standard-asr[server]"
pip install "standard-asr[audio,server]"
# uv
uv pip install "standard-asr[audio,server]"
# or, in a uv project:
uv add "standard-asr[audio,server]"
Optional extras
The core package is intentionally light — only numpy and pydantic. Everything heavy
is an opt-in extra, so you install exactly the capabilities you need and nothing else.
This is how Standard ASR stays a clean protocol layer instead of a dependency monster.
| Extra | What it adds | Pulls in |
|---|---|---|
| (core) | The protocol itself: engine discovery, capability/properties negotiation, input/output validation, and the standard-asr CLI. Decodes basic .wav with the standard library — no extra install. |
numpy, pydantic |
| audio | Battery-included audio loading. Hand over almost any audio — MP3, FLAC, OGG, M4A, raw bytes, base64 — and still drive engines that only accept NumPy arrays. Handles decoding, resampling, and channel mixing. | soundfile, scipy (plus optional system FFmpeg on PATH for the widest format coverage) |
| server | A FastAPI server exposing any compliant engine over HTTP (and WebSocket for streaming), so non-Python apps can use the ecosystem too. | fastapi, python-multipart, uvicorn, websockets |
| docs | Builds the documentation site. (For maintainers/contributors.) | mkdocs-material |
[!NOTE] Why the
audioextra matters. Audio wrangling — formats, sample rates, channels — is one of the most painful parts of using ASR. Standard ASR absorbs that pain: pass what you have, and the framework gets it into the shape the engine needs. The canonical array format isfloat32, mono, 16 kHz by default (a safe, universal target for ASR); when an engine wants a different rate or only accepts files, the conversion happens automatically — and never silently: every lossy conversion is surfaced as a structured diagnostic. The heavy decoders stay optional — basic WAV works with zero extra installs.
FastAPI server
# install with the server extra (see Installation above), then:
standard-asr serve --host 0.0.0.0 --port 8000
See docs/spec/server.md for the full HTTP/WebSocket API contract,
and docs/spec/ for the protocol specification. The WebSocket endpoint covers
the incremental-streaming path (declare an audio_format, push raw PCM frames, receive live
events); whole-input engines use the batch HTTP endpoints.
Building an engine plugin
An engine plugin is an ordinary pip-installable package that subclasses EngineBase,
declares its properties (what audio it accepts), capabilities (what features it
supports), and config (its typed, UI-discoverable settings model), and registers a
standard_asr.models entry point. The standard layer handles audio negotiation, parameter
gating, language resolution, and the sync/async bridge — you implement the model call, and
the CLI, the HTTP/WebSocket server, and the compliance checks come for free.
See docs/for_asr_dev/ for the plugin authoring guide, then validate
your implementation with:
standard-asr compliance entrypoints
FAQ
Why support different engines? Why not just use Whisper?
- Different languages have different state-of-the-art models; Whisper is strong in some, weak in others.
- GPU/hardware acceleration support varies across platforms.
- The field moves fast — today's SOTA will be replaced. Write once against Standard ASR, and countless engines (present and future) are supported automatically.
Project status & design
Pre-release, under active redesign with standard-library rigor: a normative, RFC-style
specification (docs/spec/), Pydantic v2 models, pyright --strict, 100% test coverage,
and CI across numpy 1.x/2.x and Python 3.10–3.14. The protocol's design decisions are
grounded in an in-repo survey of 30+ real ASR engines and APIs (docs/research/). The
authoritative material lives in-repo:
docs/spec/— the protocol specification.docs/research/— the engine surveys the design is tested against.CONTRIBUTING.md— dev setup, the dependency policy, and the CI channel model.
Communication
We use Zulip for development discussion: https://standard-voice.zulipchat.com
Contributing
Please read CONTRIBUTING.md before opening a pull request.
License
Apache 2.0. See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file standard_asr-0.1.1.tar.gz.
File metadata
- Download URL: standard_asr-0.1.1.tar.gz
- Upload date:
- Size: 251.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
68d716bbffae3a0869ddfdc5abcd5887c7ecae77619c3787e47891e8c1c12665
|
|
| MD5 |
f8dedf4adb869362abf8e17290f12901
|
|
| BLAKE2b-256 |
d68474ba218ea7b69021b4825c63b718159a0ba153c5104d712237db46776a81
|
Provenance
The following attestation bundles were made for standard_asr-0.1.1.tar.gz:
Publisher:
release.yml on standard-voice/standard_asr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
standard_asr-0.1.1.tar.gz -
Subject digest:
68d716bbffae3a0869ddfdc5abcd5887c7ecae77619c3787e47891e8c1c12665 - Sigstore transparency entry: 1838822544
- Sigstore integration time:
-
Permalink:
standard-voice/standard_asr@baf08ab933b0fed44720d4add9865a8b32840900 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/standard-voice
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@baf08ab933b0fed44720d4add9865a8b32840900 -
Trigger Event:
release
-
Statement type:
File details
Details for the file standard_asr-0.1.1-py3-none-any.whl.
File metadata
- Download URL: standard_asr-0.1.1-py3-none-any.whl
- Upload date:
- Size: 271.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
232c424c4c11524422e5c17564e853c9e2d9a6dd4953b4775e527f8d175440af
|
|
| MD5 |
7ed3f3e6e3583a9c28c2410e88b9b249
|
|
| BLAKE2b-256 |
72a7cabe94dcd8edd56665d00921c6ef7a72bcfc984016729eb2e8e29c731ec2
|
Provenance
The following attestation bundles were made for standard_asr-0.1.1-py3-none-any.whl:
Publisher:
release.yml on standard-voice/standard_asr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
standard_asr-0.1.1-py3-none-any.whl -
Subject digest:
232c424c4c11524422e5c17564e853c9e2d9a6dd4953b4775e527f8d175440af - Sigstore transparency entry: 1838822626
- Sigstore integration time:
-
Permalink:
standard-voice/standard_asr@baf08ab933b0fed44720d4add9865a8b32840900 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/standard-voice
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@baf08ab933b0fed44720d4add9865a8b32840900 -
Trigger Event:
release
-
Statement type: