pip install -e ".[service]"
openspeech serve --host 0.0.0.0 --port 8000 --workers 2Interactive docs are served at /docs, generated from the same route definitions.
Long edits take minutes, which is well past what an HTTP request should hold open. Work is submitted as a job, run on a worker thread, and polled or streamed. The model is loaded once for the process lifetime rather than per request.
{
"status": "ok",
"version": "0.1.0",
"engine": {"name": "auk+cache", "available": true, "sample_rate": 24000, "is_mock": false},
"queued": 0
}is_mock tells you whether real model output is being produced. Check it in deployment.
The full catalogue, generated from the registry — names, categories, parameters and their legal values, duration and chunking policy, and the upstream reference for each. Use it to build a UI that cannot offer an invalid value.
Turn a request into a plan without running anything. Instant and free.
curl -s localhost:8000/v1/plan -H 'content-type: application/json' \
-d '{"request": "clean up the noise and normalise to -16 LUFS"}'{
"request": "clean up the noise and normalise to -16 LUFS",
"language": "en",
"source": "rules",
"ops": [
{
"op": "enhance_speech",
"subtype": null,
"params": {"cleanup_mode": "denoise"},
"adjustments": [],
"instruction": "Remove only the background noise, preserve everything else, and output audio of the same length."
},
{
"op": "loudness_normalize",
"params": {"target_lufs": -16.0, "true_peak_db": -1.0},
"instruction": null
}
],
"warnings": [],
"notes": []
}instruction is null for operations that run in local DSP. adjustments records any parameter snapping; warnings records intents that matched nothing.
| Field | Type | Notes |
|---|---|---|
request |
string | Required |
language |
"en" | "zh" |
Optional; detected otherwise |
use_llm |
bool | Requires the llm extra and credentials |
Returns 422 with an explanatory detail when nothing can be planned.
multipart/form-data. Returns 202 immediately.
curl -s -X POST localhost:8000/v1/jobs \
-F 'request=remove the noise and master to -16 LUFS' \
-F 'audio=@interview.wav'{"id": "9f2c4a1b7e3d5086", "status": "queued", "created_at": 1789..., "has_output": false}| Field | Notes |
|---|---|
request |
Required |
audio |
Optional — omit for text-to-speech |
language, use_llm, seed |
Optional |
queued → running → succeeded | failed | cancelled.
Add ?progress=true for the recorded event list. A finished job carries the full run report under result: per-step verdicts, chunk counts, engine calls, repairs and quality checks.
{
"id": "9f2c4a1b7e3d5086",
"status": "succeeded",
"duration": 41.2,
"result": {
"engine": "auk",
"verdict": "pass",
"duration": 2412.5,
"steps": [
{"op": "enhance_speech(...)", "chunks": 173, "engine_calls": 174,
"verdict": "pass", "repairs": ["chunk 12: removed a DC offset of +0.0021"]}
]
}
}Server-sent events, one JSON object per line, from submission until the job finishes.
data: {"event": "step_start", "index": 0, "total": 2, "op": "enhance_speech(...)"}
data: {"event": "chunked", "chunks": 173, "budget_seconds": 13.95}
data: {"event": "chunk_start", "index": 0, "total": 173, "seconds": 13.4}
data: {"event": "retry", "attempt": 2, "reason": ["not_silent"]}
data: {"event": "step_end", "index": 0, "verdict": "pass"}
data: {"event": "done", "status": "succeeded"}
The finished WAV. 409 if the job has not succeeded, with the reason in detail.
Cancels a queued job. Running jobs are not interrupted; 409 says so.
Recent jobs, newest first. History is bounded, so a long-lived server does not grow without limit.
Objective metrics for an uploaded file — the same numbers the quality gates use.
curl -s -X POST localhost:8000/v1/measure -F 'audio=@episode.wav'{"duration": 2412.5, "sample_rate": 24000, "lufs": -16.02,
"true_peak_dbfs": -1.00, "speech_ratio": 0.83, "f0_median_hz": 118.4, ...}- One process, one model. AuK occupies GPU memory;
--workersraises job concurrency against a single loaded model. Scale across GPUs with separate processes behind a load balancer, not with more workers. - Results are files on disk. They go to a temp directory by default; pass
storagetocreate_app()for somewhere durable. - There is no authentication. Run it behind something that has some. It is an internal service, not an edge service.
- Cache the engine.
create_app()wraps the backend inCachedEngineby default; a shared cache directory means repeated work is paid for once.
from openspeech.service import create_app
from openspeech.engine import CachedEngine, create_engine
app = create_app(
engine=CachedEngine(create_engine("auk"), "/var/cache/openspeech"),
workers=2,
storage="/var/lib/openspeech/jobs",
)