Sandevistan Audio exposes two API styles on the same FastAPI service:
- Native asynchronous endpoints for durable jobs, queue visibility, progress, retries, and result artifacts.
- OpenAI-compatible synchronous audio endpoints for clients that already speak the OpenAI wire format.
The interactive bilingual contract at /docs and the generated /openapi.json are authoritative for field-level schemas. This guide explains the lifecycle rules that clients must preserve.
The service entrypoints automatically read the project-root .env; exported environment variables take precedence. This affects the service and its children, not the calling shell or remote API clients. Set AUDIO_INTEL_BASE_URL in the client environment to its actual reachable URL, including the configured protocol and port (for example http://127.0.0.1:20815); the examples below retain http://127.0.0.1:20810 as the default. Use service.sh status or service.cmd status to inspect the running endpoint. The bind address 0.0.0.0 is not a remote client address.
Provide the same AUDIO_INTEL_API_KEY separately to authenticated clients from your existing credential source. Starting the service does not populate that variable in your terminal. Swagger uses the origin serving /docs and the relative /openapi.json; no deployment-specific server URL is embedded in the contract. See the configuration rules.
BASE_URL=${AUDIO_INTEL_BASE_URL:-http://127.0.0.1:20810}Authentication is disabled by default. When AUDIO_INTEL_API_KEY is configured, CLI and service clients send:
Authorization: Bearer <key>
The bundled browser exchanges the key for an opaque HttpOnly same-origin session cookie. Pending voiceprint polling stops when the session expires and resumes after successful login if samples are still pending. Do not put the raw key in URLs or browser storage.
GET /api/v1/health is always public and intentionally minimal. GET /api/v1/auth/session may also be called before login to discover whether authentication is required. When project-managed HTTPS is enabled, the public GET /api/v1/tls/bootstrap, GET /api/v1/tls/root-ca.cer, and GET /api/v1/tls/root-ca.pem endpoints expose only the configured public CA and its SHA-256 fingerprint; CA downloads return 404 when unavailable. Detailed system, model, worker, media, and task surfaces remain protected. See the local HTTPS guide for certificate handling and trust verification.
The version returned by health and system responses is also used as OpenAPI info.version. A clean tagged checkout reports its X.Y.Z release version; later source commits use SemVer build metadata such as X.Y.Z+3.g8adfe46, with .dirty appended for tracked local modifications. A shallow or stale-tag checkout may omit the commit distance but retains the commit hash. Source archives without Git metadata report their embedded release fallback. Treat the full string as build identity rather than assuming that every source checkout equals the latest release artifact.
Every person belongs to one group, exposed as group_id. Existing/new people default to voiceprint_ungrouped (未分组 / Ungrouped), which cannot be renamed or deleted. Names are normalized and unique, 1–80 characters. Deleting another group atomically moves its members to Ungrouped; it retains people and audio. Personnel identity remains globally unique by normalized name plus note.
| Operation | Endpoint / Contract |
|---|---|
| List / create groups | GET / POST /api/v1/voiceprints/groups; creation body {"name":"Team"} |
| Rename / delete group | PATCH / DELETE /api/v1/voiceprints/groups/{group_id} |
| Move people | POST /api/v1/voiceprints/people/batch-move, {"person_ids":["voice_…"],"group_id":"group_…"}; all-or-nothing validation |
| Delete people | POST /api/v1/voiceprints/people/batch-delete, {"person_ids":["voice_…"],"purge":true}; per-person deleted, status, detail, protecting active imports and readers |
Audio and OpenAI-compatible submissions accept optional JSON-array form text voiceprint_group_ids; video submissions accept a JSON array. Omission means all groups, including Ungrouped. Empty/unknown groups are rejected when matching is enabled. An empty group is valid and contributes no candidates. The accepted request stores group names and eligible person IDs; later moves/additions do not alter scope. Missing people are skipped, while names and samples are read at execution. Retry preserves the request and idempotent replay preserves its first snapshot even after group deletion. Historical requests without snapshots retain legacy behavior. Scope snapshots appear only in per-job detail, never global summary/SSE.
BASE_URL=${AUDIO_INTEL_BASE_URL:-http://127.0.0.1:20810}
REQUEST_KEY=$(python3 -c 'import uuid; print(uuid.uuid4())')
VOICEPRINT_GROUP_IDS=$(curl --fail-with-body -sS -H "Authorization: Bearer $AUDIO_INTEL_API_KEY" "$BASE_URL/api/v1/voiceprints/groups" | jq -c '[.items[0].id]')
curl --fail-with-body -sS -H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
-H "Idempotency-Key: $REQUEST_KEY" -F file=@meeting.wav -F compute_device=cpu \
-F "voiceprint_group_ids=$VOICEPRINT_GROUP_IDS" "$BASE_URL/api/v1/asr/jobs"中文:分组删除只移动人员,批量删除人员需明确确认。ASR 提交时固定人员范围,后续移动不影响排队任务;浏览器默认使用全部,手动多选长期记忆,失效选择不自动扩大范围。
POST /api/v1/jobs/{job_id}/transcript-export is a protected synchronous download for successful audio/video ASR. It needs no Idempotency-Key; cookie clients retain same-origin write checks. Body: format (pdf, json, txt, srt, vtt; default pdf), redact (default true), and replacements (default [], at most 100 unique {source,target} pairs, 8000 characters per value). Undeclared fields and invalid/duplicate rules return 422. PDF saturation returns 429 with Retry-After; oversized exports return 413.
Names use uppercase Latin word or Chinese pinyin initials: Vince Carter → VC, 刘亦菲 → LYF, Liu Yifei → LY, Marco → M. Collisions become VC-1, VC-2 in speaker order. Generic speaker numbers remain. Notes mask the middle ceil(n/2) Unicode characters: 01234567 → 0167, 广东移动有限公司 → 广东公司. Literal keyword replacements affect transcript text and timed words, are case-sensitive, longest-first and single-pass without chaining. Matched word spans merge in the export copy and retain their full time range. Empty targets delete the matched text.
Redacted JSON contains transcript fields, speakers, segments/timed words and safe timing/model/video metadata; auxiliary identity/hotword snapshots and artifact paths are omitted. Redacted titles/file names are generic. Download transformations never modify stored jobs or historical artifacts. Audio and video images remain unchanged; names in body text only change when covered by explicit keywords. redact=false disables all transformations, including keyword rules. The original artifact and GET/HEAD PDF routes remain raw backward-compatible surfaces.
BASE_URL=${AUDIO_INTEL_BASE_URL:-http://127.0.0.1:20810}
JOB_ID=${JOB_ID:?export the completed ASR job ID first}
curl --fail-with-body -sS -H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"format":"txt","redact":true,"replacements":[{"source":"广东移动","target":"gy"}]}' \
"$BASE_URL/api/v1/jobs/$JOB_ID/transcript-export" -o transcript.txtimport os
from pathlib import Path
import httpx
base = os.getenv("AUDIO_INTEL_BASE_URL", "http://127.0.0.1:20810")
headers = {"Authorization": "Bearer " + os.environ["AUDIO_INTEL_API_KEY"]} if os.getenv("AUDIO_INTEL_API_KEY") else {}
response = httpx.post(base + "/api/v1/jobs/" + os.environ["JOB_ID"] + "/transcript-export", headers=headers,
json={"format": "json", "redact": True, "replacements": [{"source": "广东移动", "target": "gy"}]}, timeout=300)
response.raise_for_status()
Path("transcript.json").write_bytes(response.content)中文:界面全部转写下载默认脱敏。替换编辑器按行 原词=替换词,按第一个等号拆分,忽略空行、允许空替换词;错误显示行号。独立 localStorage audio-intel:transcript-export:v1 保存开关和原始编辑内容,刷新、重开、退出登录及资料库替换后保留;恢复默认导出设置开启脱敏并清空规则。ASR 分组偏好使用 v4,兼容迁移旧版本。
The following submission endpoints require an Idempotency-Key header containing 8–128 characters:
POST /api/v1/asr/jobsPOST /api/v1/asr/video-jobsPOST /api/v1/tts/clone-referencesPOST /api/v1/tts/jobsPOST /api/v1/tts/sequence-jobsPOST /api/v1/tts/document-imports(returns a parser import, not an inference job)POST /api/v1/tts/document-jobsPOST /api/v1/voiceprints/people/{person_id}/samples/upload
The independent library-transfer upload also requires an Idempotency-Key; its receipt, preview and application lifecycle is described under Library export and import.
Generate one key per logical submission and reuse it after timeouts, disconnects, or 429 responses:
REQUEST_KEY=$(python3 -c 'import uuid; print(uuid.uuid4())')| Result | HTTP status | Client action |
|---|---|---|
| First accepted submission | 202 |
Store the returned job and status URL |
| Same key and same request | 200 |
Treat the returned original job as the accepted submission |
| Same key with changed input | 409 |
Generate a new key only for a genuinely new logical request |
| Admission rejected | 429 |
Honor Retry-After and retry with the same key |
Admission protects per-kind queue limits, concurrent uploads, and minimum free disk. Reservations are process-local, so the supported service runs one API process.
GET /api/v1/capabilities and GET /api/v1/system expose deployment.profile, deployment.default_compute_device, and deployment.gpu_runtime_installed. Full deployments default omitted device fields to GPU. CPU-only deployments default them to CPU and mark every model-scoped GPU capability unavailable with gpu_runtime_not_installed; an explicit GPU submission returns that stable 503 problem code before a job is accepted. Video uploads have no device setting: their device admission is checked later at POST /api/v1/asr/video-jobs, after uploading. Physical GPU telemetry may still appear under system.hardware.gpu and does not imply that a GPU inference runtime is installed.
ASR_KEY=$(python3 -c 'import uuid; print(uuid.uuid4())')
curl --fail-with-body -sS \
-H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
-H "Idempotency-Key: $ASR_KEY" \
-F file=@meeting.wav \
-F language=Auto \
-F speaker_count=auto \
-F model=qwen3-asr-0.6b \
-F diarize=true \
-F align=true \
-F use_voiceprint_library=true \
-F compute_device=cpu \
-F accelerate_single_task=true \
"$BASE_URL/api/v1/asr/jobs"Omit the Authorization header when authentication is disabled. Supported explicit aligned languages are Chinese, English, Cantonese, French, German, Italian, Japanese, Korean, Portuguese, Russian, and Spanish. Auto can detect other model languages, but those results remain at sentence/segment timestamp granularity.
Saved hotword lists are selected through one comma-separated hotword_list_ids form field as described by /docs. A task stores an immutable hotword snapshot, so later library edits do not rewrite history.
TTS_KEY=$(python3 -c 'import uuid; print(uuid.uuid4())')
curl --fail-with-body -sS \
-H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
-H "Idempotency-Key: $TTS_KEY" \
-F text='Hello from a private speech workstation.' \
-F language=English \
-F voice_mode=preset \
-F speaker=Ryan \
-F model=qwen3-tts-0.6b \
-F response_format=wav \
-F compute_device=cpu \
-F accelerate_single_task=true \
"$BASE_URL/api/v1/tts/jobs"Read GET /api/v1/capabilities before displaying TTS controls. The 0.6B models and Base clone checkpoints reject natural-language instructions. The 1.7B CustomVoice preset path accepts an optional instruct; 1.7B VoiceDesign requires one. Unsupported combinations return 422 and are never silently ignored.
POST /api/v1/tts/sequence-jobs accepts JSON with 1–100 ordered items sharing one model, device, language, and voice mode. Read tts.sequence_jobs before submitting: require supported=true and a supported contract_version (currently 1), and respect max_items and max_total_chars. Fall back to single-item jobs when the capability is absent, unsupported, or uses a contract version the client does not understand.
- Only
presetandvoiceprintmodes are supported. Eachpresetitem requires an officialspeaker; optional per-iteminstructis supported only by the 1.7B model. Omitvoiceprint_sample_idin this mode. - Each
voiceprintitem requires an existing ready, TTS-eligiblevoiceprint_sample_id; omitspeakerandinstruct. Different items may use different samples. Accepted jobs retain their own reference audio and metadata snapshots. - Item
idvalues must be unique within the request, contain 1–64 characters, and match[A-Za-z0-9._-]+. Each text must be nonempty after trimming leading/trailing whitespace. The sum of these trimmed text lengths must not exceedmax_total_chars(configured byAUDIO_INTEL_MAX_TTS_CHARS). - Output is always one WAV per item. Device omission follows the deployment default;
accelerate_single_taskdefaults totrueand can be disabled explicitly.
Generate a key once for this logical submission; reuse SEQUENCE_KEY and the same body when retrying the curl command after a timeout or 429:
SEQUENCE_KEY=$(python3 -c 'import uuid; print(uuid.uuid4())')curl --fail-with-body -sS \
-H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
-H "Idempotency-Key: $SEQUENCE_KEY" \
-H 'Content-Type: application/json' \
-d '{"model":"qwen3-tts-0.6b","language":"English","voice_mode":"preset","compute_device":"cpu","items":[{"id":"intro","text":"Welcome.","speaker":"Ryan"},{"id":"reply","text":"Let us begin.","speaker":"Aiden"}]}' \
"$BASE_URL/api/v1/tts/sequence-jobs"For a voiceprint sequence, use a new logical submission key and replace the body with the following JSON, substituting real eligible sample IDs from the voiceprint library:
{
"model": "qwen3-tts-0.6b",
"language": "English",
"voice_mode": "voiceprint",
"compute_device": "cpu",
"items": [
{"id": "intro", "text": "Welcome.", "voiceprint_sample_id": "sample_0123456789abcdef"},
{"id": "reply", "text": "Let us begin.", "voiceprint_sample_id": "sample_fedcba9876543210"}
]
}Sequence request objects and each item reject undeclared fields with 422 (application/json, detail[].type=extra_forbidden). Do not send speed, pitch, sampling controls, or response_format; WAV is fixed by contract v1.
A sequence occupies one TTS queue position. Progress counts synthesis chunks across all items. Failure or cancellation applies to the entire job; retrying it regenerates all items from the persisted request, with no per-item resume or partial result delivery.
After success, job detail contains result.sequence; GET /api/v1/jobs/{job_id}/result returns the result directly, with sequence at its root. This excerpt shows the ordered item-to-artifact mapping:
{
"sequence": {
"contract_version": 1,
"items": [
{"id": "intro", "artifact_name": "item-0000.wav", "duration": 1.5, "sample_rate": 24000},
{"id": "reply", "artifact_name": "item-0001.wav", "duration": 1.5, "sample_rate": 24000}
]
}
}Each artifact_name matches an entry in artifacts[].name. Use the returned names rather than deriving them from item IDs. With JOB_ID set to the accepted job ID, download the first item after success (requires jq):
ARTIFACT_NAME=$(curl --fail-with-body -sS \
-H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
"$BASE_URL/api/v1/jobs/$JOB_ID/result" | jq -er '.sequence.items[0].artifact_name')
curl --fail-with-body -sS \
-H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
"$BASE_URL/api/v1/jobs/$JOB_ID/artifacts/$ARTIFACT_NAME" -o first-item.wavSequence-specific 422 problem codes include:
| Code | Correction |
|---|---|
duplicate_tts_sequence_item |
Use distinct item IDs |
invalid_tts_sequence_text |
Supply nonempty text after trimming |
tts_sequence_too_large |
Split the request to respect the total character limit |
unknown_tts_speaker |
Select an official preset speaker |
invalid_tts_sequence_item |
Supply the fields required by the selected mode and omit conflicting fields |
voiceprint_sample_unavailable |
Select an existing ready sample with available audio and transcript |
Model/control validation and the shared 200/202/409/429/503 contracts also apply.
- Submit a reference audio file to
POST /api/v1/tts/clone-referenceswith its own idempotency key. - Poll the returned ASR analysis job until it succeeds.
- Review the detected reference text and language.
- Submit
POST /api/v1/tts/jobswithvoice_mode=inline_cloneand the resultingreference_job_id.
The older direct reference_audio plus reference_text TTS form remains compatible, but the analysis-job flow is preferred because it is inspectable and reusable from task history.
Submission responses provide a job ID, status_url, and polling guidance. Read job detail until it enters a terminal state:
curl -H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
"$BASE_URL/api/v1/jobs/JOB_ID"Clients should preserve the response ETag and send If-None-Match on later polls. 304 Not Modified means neither the task nor its same-kind queue context has changed. Follow poll_after_seconds; do not use high-frequency database polling to simulate animation.
For live updates, use:
GET /api/v1/jobs/{job_id}/eventsfor one full task stream.GET /api/v1/eventsfor global summary updates.
The global stream starts with a recent summary snapshot, then sends semantic update events and lightweight {} heartbeats. It never contains full request or result JSON. Reconnect and reconcile with the first snapshot or the job detail endpoint; SSE is not an event-history log.
progress is monotonic best-effort progress. Check progress_detail.basis: estimated values are not exact completion counts. Model loading publishes start and completion boundaries only; the service does not fabricate loading percentages or heartbeat activity while a backend blocks.
After a job succeeds, fetch the complete result and artifacts:
curl -H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
"$BASE_URL/api/v1/jobs/JOB_ID/result"
curl -H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
"$BASE_URL/api/v1/jobs/JOB_ID/artifacts/result.srt" -o result.srtASR source media is available at GET /api/v1/jobs/{job_id}/source, supports HTTP Range requests, and accepts ?download=true to force a download.
新完成的 ASR 任务在结果中提供 playback_url,指向 GET /api/v1/jobs/{job_id}/playback。它返回增强前保存的 16 kHz、单声道、PCM16 原音 WAV,使片段、字词和波形跳转与识别时间轴一致,避免 VBR MP3 随机跳转偏差。该接口受 Bearer / 浏览器会话鉴权保护,支持 Range(206 / 416);非 ASR 或尚未成功的任务返回 409,音频缺失返回 404。历史任务不补生成文件,缺少此字段时继续使用原音源。原文件下载和字幕时间戳保持不变。
New successful ASR results include playback_url, pointing to GET /api/v1/jobs/{job_id}/playback. This serves the saved unenhanced 16 kHz mono PCM16 WAV on the recognition timeline, avoiding inaccurate VBR MP3 seeks for segments, words and waveforms. Bearer or browser-session authentication is required. Range requests support 206 / 416; non-ASR or unsuccessful jobs return 409, and missing audio returns 404. Historical jobs are not backfilled and continue using their original source when this field is absent. Original downloads and transcript timestamps are unchanged.
GET /api/v1/jobs/{job_id}/playback?download=true 下载与播放器“原音”一致的 WAV,音频和视频任务均适用。
download 为可选 boolean,默认 false;true 返回 Content-Disposition: attachment,文件名为安全处理后的任务名(去扩展名)加 -original.wav。
内容与普通回放逐字节相同,不重新处理或创建副本;鉴权、Range 及 404/409/416 语义保持一致。
它与 /source?download=true 不同:后者提供上传的原始音频或视频文件。
Optional query download=true serves the same unenhanced WAV as an attachment for audio and video jobs.
The default is false. The filename uses the sanitized task stem plus -original.wav; long stems are truncated while preserving the WAV suffix.
No audio is reprocessed, copied or backfilled. Authentication and Range/error behavior remain unchanged.
/source?download=true continues to download the original upload, including the video container for video jobs.
播放 WAV 随任务删除;每小时音频约增加 115 MB 存储。它不加入字幕导出文件列表,也不改变浏览器偏好或草稿的保存期限。
The playback WAV is deleted with its job and adds approximately 115 MB per audio hour. It is separate from transcript export artifacts and does not change browser preference or draft lifetimes.
# JOB_ID must identify a successful new ASR task.
curl --fail-with-body \
-H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
-H 'Range: bytes=0-43' \
"$AUDIO_INTEL_URL/api/v1/jobs/$JOB_ID/playback" -o playback-header.bin
# Download the complete unenhanced WAV.
curl --fail-with-body -H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
"$AUDIO_INTEL_URL/api/v1/jobs/$JOB_ID/playback?download=true" -o original.wavPATCH /api/v1/jobs/{job_id}/speakers/{speaker_id} edits a successful ASR result. Its JSON body requires name and accepts optional note. Names must be nonempty after trimming and are retained in full, including library names longer than 80 characters. Notes follow the library's 20-character normalized limit: omission preserves the current note, while null, empty or whitespace-only input clears it. Results expose optional active speakers[].name and note alongside label; historical results may lack these fields.
Automatic voiceprint recognition lets each detected speaker select its best match above the existing threshold, so several clusters can match one person. Automatic results and manual saves merge named speakers within a task by normalized name + note (NFKC, whitespace folding, case folding). Different notes and unnamed default speakers remain separate. Each merged group retains the first ID in the result's speaker list; segment and word speaker references use that ID. Segment IDs, order, text and timestamps are preserved. Later edits apply to the entire merged person. Match scores retain the representative record's original snapshot; voiceprint_library.matched_speakers counts matches before merging.
The response is the complete updated result (200). Refresh local speaker IDs from it: a merged-away ID returns 404. Invalid names or notes return 422; non-ASR, unfinished or conflicting/deleting tasks return 409. File/database save failures return 503 and may be retried. Saving refreshes the task's JSON/TXT/SRT/VTT exports; playback audio, library people, existing samples and other historical jobs keep their snapshots. Existing tasks are merged only on explicit save, with no startup or read-time bulk rewrite.
The browser dialog can copy an existing library person's name and note into editable fields, including people without samples. Choosing someone without a note clears the prior note. Manual entry remains available during library loading or errors, and loading errors offer retry. Selection is dialog-local and does not add a persistent person-ID binding or change browser-storage lifetimes.
Choose one example below, using a speaker ID from the latest result of the intended completed task. Editing changes that task and its exports.
set -euo pipefail
BASE_URL=${AUDIO_INTEL_BASE_URL:-http://127.0.0.1:20810}
JOB_ID=${JOB_ID:?export the completed ASR job ID first}
SPEAKER_ID=${SPEAKER_ID:?export a current speaker ID from its result first}
AUTH=(); if [[ -n "${AUDIO_INTEL_API_KEY:-}" ]]; then AUTH=(-H "Authorization: Bearer $AUDIO_INTEL_API_KEY"); fi
curl --fail-with-body -sS "${AUTH[@]}" -X PATCH \
"$BASE_URL/api/v1/jobs/$JOB_ID/speakers/$SPEAKER_ID" \
--json '{"name":"张三","note":"研发一部"}'API clients can select a person from GET /api/v1/voiceprints/people and copy just its name and note. The saved values are task-local snapshots; later library edits do not rewrite them. Using the same environment and AUTH setup above:
PERSON_ID=${PERSON_ID:?export the existing library person ID first}
PERSON=$(curl --fail-with-body -sS "${AUTH[@]}" \
"$BASE_URL/api/v1/voiceprints/people" \
| jq -ce --arg person_id "$PERSON_ID" \
'.items[] | select(.id == $person_id) | {name, note: (.note // null)}')
curl --fail-with-body -sS "${AUTH[@]}" -X PATCH \
"$BASE_URL/api/v1/jobs/$JOB_ID/speakers/$SPEAKER_ID" --json "$PERSON"Important job endpoints include:
GET /api/v1/jobs— stable server-side pagination and filters; summaries only.GET /api/v1/jobs/{job_id}— full request and result detail.POST /api/v1/jobs/{job_id}/cancel— begin safe cancellation.POST /api/v1/jobs/{job_id}/retry— retry a terminal task.DELETE /api/v1/jobs/{job_id}?purge=true— permanently remove one eligible task and its files.POST /api/v1/jobs/batch-delete— delete up to 100 IDs with per-item results.
Retry requeues only a still-failed or cancelled job, atomically, at the tail of its same-kind queue. It preserves the job ID, request snapshots, and accumulated processing time. Success returns 200; a state conflict returns 409, and queue capacity, submission concurrency, or disk admission rejection returns 429 with Retry-After. Retry requires no Idempotency-Key.
curl --fail-with-body -i -X POST \
-H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
"$BASE_URL/api/v1/jobs/$JOB_ID/retry"Cancellation is terminal only after the task executor and all descendants have exited and temporary files have been cleaned. A running task cannot be permanently purged while its process tree may still write files.
Use GET /api/v1/capabilities instead of hard-coding model-dependent controls, device availability, upload limits, language lists, or speaker limits.
Main library surfaces:
GET|POST /api/v1/asr/hotword-listsPATCH|DELETE /api/v1/asr/hotword-lists/{item_id}GET|POST /api/v1/voiceprints/peoplePATCH|DELETE /api/v1/voiceprints/people/{person_id}POST /api/v1/voiceprints/people/{person_id}/samples/from-asrPOST /api/v1/voiceprints/people/{person_id}/samples/uploadDELETE /api/v1/voiceprints/people/{person_id}/samples/{sample_id}GET /api/v1/voiceprints/samples/{sample_id}/audioGET|POST /api/v1/tts/voicesDELETE /api/v1/tts/voices/{voice_id}
samples/from-asr accepts completed audio and video ASR jobs with the existing JSON body {"job_id":"...","segment_ids":[0,2]}. IDs refer to original result.segments, not merged presentation paragraphs. All selected segments must belong to one speaker; each becomes one WAV sample. Duplicate imports return 409. Video samples are cut from the saved output/playback.wav on the transcription timeline; missing playback returns 404 without falling back to container timestamps. Audio jobs retain their existing source-recording behavior.
中文:视频入库复用同一接口和原始语句 ID,每句单独成样本,重复入库返回 409。视频按任务保存的播放音频截取,缺失返回 404,不会回退到原视频时间轴或自动补写历史文件。
Completed jobs keep speaker-name, hotword, device, and model snapshots. Later library or hardware changes do not rewrite historical results.
The compatibility endpoints wait synchronously and are best suited to short requests. /v1/audio/speech enforces the same trimmed-text limit as native TTS: 1 through GET /api/v1/capabilities → limits.max_tts_chars characters. Blank or overlong input returns 422 before a job is created:
curl --fail-with-body -sS \
-H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
-F file=@meeting.wav \
-F model=qwen3-asr-0.6b \
-F prompt='Project meeting' \
-F response_format=verbose_json \
-F compute_device=cpu \
"$BASE_URL/v1/audio/transcriptions"
curl --fail-with-body -sS \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
-d '{"model":"qwen3-tts-0.6b","input":"Hello","voice":"Ryan","language":"English","response_format":"wav","compute_device":"cpu"}' \
"$BASE_URL/v1/audio/speech" -o speech.wavGET /v1/models lists compatible model identifiers. OpenAI-compatible instructions only maps to the 1.7B preset-voice path; use the native API for VoiceDesign and durable long-running work.
| Status | Meaning |
|---|---|
401 |
Missing or invalid Bearer/session authentication |
409 |
Idempotency key reused with different content, or an ineligible job state for retry |
422 |
Unsupported language, model, voice mode, control, or validation input |
429 |
Submission concurrency, queue capacity, or disk admission limit |
503 |
Requested GPU or model revision is unavailable; CPU-only deployments use the stable gpu_runtime_not_installed problem code for explicit GPU requests |
GPU requests never silently fall back to CPU. Retry 429 using its Retry-After value and, for keyed submissions, the original idempotency key. Treat 503 as a capability/configuration issue and explicitly select compute_device=cpu if that matches user intent.
People retain stable IDs and are unique by normalized name + note (NFKC, whitespace folding, case folding). Notes remain optional and limited to 20 characters; null, empty, and whitespace-only notes are equivalent. Creating or updating a duplicate pair returns 409. System full-name and surname-free hotword lists deduplicate terms, exclude notes, and retain a shared name while any matching person opts in. Custom list names remain unique.
Every sample response includes persistent name. Upload accepts an optional name base; otherwise it uses the filename without extension. The recording UI supplies a timestamp name, and ASR imports use task name plus segment number. Creation adds (2), (3), etc. for collisions, truncating the base to keep the total at most 80 characters. Existing samples retain their pre-upgrade library number as a fixed name.
PATCH /api/v1/voiceprints/people/{person_id}/samples/{sample_id} accepts only {"name":"Meeting room recording"} and returns the updated sample (200). Normalized names must contain 1–80 characters, without control characters, and be unique within that person. Duplicate names return 409; invalid names or extra fields return 422; missing samples or mismatched person IDs return 404. Samples in every state can be renamed without changing audio, transcripts, or analysis jobs.
This example requires jq and a local reference.wav. Keep VOICEPRINT_KEY for retries of the same upload:
PERSON_ID=$(curl --fail-with-body -sS -H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
-H 'Content-Type: application/json' -d '{"name":"Alex Li","note":"Research"}' \
"$BASE_URL/api/v1/voiceprints/people" | jq -er '.id')
VOICEPRINT_KEY=$(python3 -c 'import uuid; print(uuid.uuid4())')
SAMPLE_ID=$(curl --fail-with-body -sS -H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
-H "Idempotency-Key: $VOICEPRINT_KEY" -F file=@reference.wav \
-F compute_device=cpu -F name='Meeting room' \
"$BASE_URL/api/v1/voiceprints/people/$PERSON_ID/samples/upload" | jq -er '.sample.id')
curl --fail-with-body -sS -X PATCH -H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
-H 'Content-Type: application/json' -d '{"name":"Quiet meeting room"}' \
"$BASE_URL/api/v1/voiceprints/people/$PERSON_ID/samples/$SAMPLE_ID"TTS cloning selects a stable sample ID. New single and sequence requests snapshot voiceprint_person_name, voiceprint_person_note, and voiceprint_sample_name (inside voiceprint_references for sequences). Renaming the library does not rewrite accepted jobs or invalidate same-request idempotent replay. Failed/cancelled retries use the complete persisted request. Legacy POST /api/v1/tts/voices returns 409 when a name matches multiple people; use the native voiceprint endpoints with an explicit person ID instead.
The automatic clone-reference limit comes from limits.max_clone_reference_seconds (currently 15); manual voiceprint ranges are advertised separately in tts.model_capabilities[].controls.reference_range. Long references use the beginning through the last complete word within that limit, leaving the original sample intact. Before submission, the UI displays the limit; after a single clone completes, reference_duration_original and reference_duration_used provide measured seconds, and reference_truncated indicates truncation. Missing historical durations remain unknown. Ordered sequence results retain contract v1.
Use Document TTS for EPUB, TXT, Markdown, text PDF, DOCX, XLSX and PPTX. Upload a document, poll its import to ready, request a segmentation preview, then submit its revision and selected IDs to /api/v1/tts/document-jobs. This accepts up to the capability-advertised section count (2,000 by default) with either multipart or URL-encoded forms. Ordinary TTS retains its 50,000-character limit.
The browser bulk-removal flow invokes the existing single-import DELETE for each selected entry, reports individual outcomes and retains failures for retry; there is no atomic bulk-delete endpoint. Imports are retained until explicitly deleted. GET /api/v1/tts/document-imports?offset=0&limit=20 preserves the array response and includes size_bytes (source) and storage_bytes (source, parsed text and log). Omit pagination to list all imports. Reuse an existing ready import without uploading again. DELETE .../{identifier} rejects active parsing or snapshot copying with 409 and does not remove already submitted jobs.
POST /api/v1/tts/document-imports/{identifier}/retry requeues only a failed parser import using the saved file (202; 409 if no longer failed or source missing). It shares upload/parse admission (429 with Retry-After), needs no Idempotency-Key, and never silently retries a malformed document. Uploads authenticate and reserve capacity before reading the body, stream directly to a temporary source, and remove partial files on failure. Same-key upload retries validate the complete file hash and reuse the original import.
segmentation_mode=auto prefers reliable structure; target_section_chars is only a fallback for unstructured text or a preface, not a cap on structural chapters. length uses the target throughout. basis identifies a structural source or a text boundary, not a second segmentation-mode parameter.
The typed section response is stable before and after executor initialization: index is the original document section number, position is the selected job order, start/end and their start_offset/end_offset aliases address the canonical text. char_count, basis, optional page bounds, state, retries, artifact and updated_at are always represented. Whitespace-only sections are merged losslessly; affected old previews return 409 on submission and must be reviewed again. Persisted job snapshots remain unchanged.
New document jobs persist server-owned request.document.audio_metadata; callers cannot supply it. All section downloads and the complete MP3 share the album, using the saved document title, voice/person name and UTC creation minute; selected section order supplies track numbers. Historical jobs are not automatically retagged. See album rules and the offline single-job tool.
ZIP and complete MP3 downloads are streamed without stored exports. Handle 401/404/409/429 before consuming the body and honor Retry-After (also retry_after_seconds in problem JSON). After the response has begun, transport failures terminate the download; retry from the beginning. The browser UI retains the page on an error and delegates large downloads to the browser download manager without a whole-file Blob.
GET /api/v1/jobs/{job_id}/artifacts/{name}/waveform 使用浏览器会话或 Bearer 鉴权。name 必须来自成功任务的 artifacts[].name,并进行 URL 编码。返回 { "artifact_name": "speech.wav", "duration": 1.5, "waveform": [0.1, 0.4, 0.2] },其中峰值按时间均匀分布,范围为 0–1,最多 240 项。
单段文本、文档章节与有序序列在音频生成后准备波形。历史音频或缓存缺失时分块解码补算;同一文件合并计算,最多并发补算两段。尚未成功返回 409,文件缺失或越界返回 404,非 WAV/MP3/FLAC 返回 415,无法解码返回 422,计算繁忙返回 429 和 Retry-After: 2。补算期间清理任务返回 409。缓存随任务删除,既有结果字段和契约版本保持兼容。
The authenticated endpoint above returns uniformly timed peak amplitudes for each audio artifact. New single, document and sequence audio is precomputed; historical audio is decoded incrementally on demand and cached. At most two cold computations run concurrently; busy clients should honor Retry-After. Waveform files are removed with their job. Existing result schemas and contract versions remain compatible.
可执行示例 / Executable example:
.runtime/api/bin/python scripts/tts_document.py manuscript.md --waveformPOST /api/v1/tts/jobs and /api/v1/tts/document-jobs accept optional form fields reference_start_seconds and reference_end_seconds. Both must be supplied together, only with voice_mode=voiceprint. Sequence requests accept the same numeric fields on each items[] entry. The requested interval must lie within the original sample and be 3–30 seconds long; it may start anywhere, including beyond the first 30 seconds. Non-finite values, incomplete pairs, duplicate form endpoints, unsupported modes and out-of-bounds ranges return 422.
Omit both endpoints to keep the existing automatic beginning-of-sample selection, up to 15 seconds. Browser-saved ranges are local preferences, never server defaults for API callers. Explicit ranges are part of request identity: replaying the same key and interval returns 200, while changing the interval with that key returns 409. Old requests without these fields remain replayable.
Audio and transcript are narrowed together to complete aligned words inside the requested interval. The effective interval must still contain 3–30 seconds; otherwise select a wider range. Existing word timestamps are reused. Missing or inconsistent alignment is recomputed offline in the TTS worker from the submitted snapshot; unavailable language/alignment or insufficient complete speech fails the job explicitly. No alternate interval is silently substituted. The original library sample is not cropped or rewritten.
Single/document results, and each voiceprint sequence result item, expose reference_start_seconds_used, reference_end_seconds_used, and reference_text_used, alongside reference_duration_original, reference_duration_used, and reference_truncated. Times are relative to the original sample. Retries use submission-time snapshots; sequence contract v1 and output order are unchanged. The same sample can have different intervals in different sequence items.
curl -H "Authorization: Bearer $AUDIO_INTEL_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-F 'text=这次使用样本中间的声音。' -F 'voice_mode=voiceprint' \
-F "voiceprint_sample_id=$SAMPLE_ID" -F 'compute_device=gpu' \
-F 'reference_start_seconds=10' -F 'reference_end_seconds=40' \
"$BASE_URL/api/v1/tts/jobs"For /api/v1/tts/sequence-jobs, supply an item such as:
{"id":"middle","text":"使用中间区间。","voiceprint_sample_id":"sample_id","reference_start_seconds":10,"reference_end_seconds":40}The document CLI supports the same selection:
.runtime/api/bin/python scripts/tts_document.py manuscript.md \
--base-url "$BASE_URL" --voiceprint-sample-id "$SAMPLE_ID" \
--reference-start-seconds 10 --reference-end-seconds 40GET /api/v1/voiceprints/samples/{sample_id}/audio/waveform returns the same {artifact_name,duration,waveform} shape as artifact waveforms, with at most 240 timed peaks. It requires Bearer or browser-session authentication. Fetch only when the editor is opened; honor 429/Retry-After, allow retry, and use the protected sample audio endpoint for playback and Range requests. Waveform caches are isolated per sample location and removed with the sample. Deletion during waveform reading returns 409; retry after the reader finishes.
真实 TTS 生成默认启用逐文本块保护,覆盖单条、文档、有序序列和 OpenAI 兼容请求。正常调用保持固定模型、精度、采样配置和分块方式。预算由目标文本 token 数计算:min(8192, max(750, ceil(tokens * 13.5)));无 tokenizer 计数时使用明确标记的字符估算。预算不是精确时长预测。达到预算但没有自然 EOS、空音频或非有限波形不会作为成功产物发布。
Each failing original text chunk gets at most three additional singleton generation calls with the same voice/reference and default sampling parameters. Only retries may remove clearly identified table-of-contents dot leaders; title/page-number content remains intact. A durable ledger retains the allowance through document resource recovery. User-initiated whole-job retry starts a new allowance; ordered sequences still retry the entire saved request. Exhaustion fails the job with error_code=TtsGenerationGuardError; completed document sections remain reusable, while the incomplete section is not published.
progress_detail.stage_code=tts_chunk_retry uses current/total and unit=attempt for retry count; codec-frame activity remains observed and overall progress does not decrease. Job detail/result responses may include:
{"generation_guard":{"version":1,"checked_chunks":8,"retried_chunks":1,"retry_attempts":2,"recovered_chunks":1}}Counters describe original chunks actually checked in the current job attempt; reused historical sections are not newly checked. Missing generation_guard means no recorded guard coverage (including older results and mock mode), never an implied successful check. Recovery counters do not constitute comprehensive audio-quality assessment. No new submission parameters or sampling controls are exposed. Global job lists and SSE remain summary-only.
Existing authenticated polling clients can inspect the optional result without changing submission:
curl --fail-with-body -sS "${AUTH[@]}" "$BASE_URL/api/v1/jobs/$JOB_ID/result" \
| jq 'if has("generation_guard") then .generation_guard else {status:"not_recorded"} end'对需要裁剪的参考音频,默认和手动选择均校验对齐时间及文本映射;无效对齐只重新计算一次,仍无效则明确失败。默认最多 15 秒、手动 3–30 秒不变;默认短样本不受手动区间的 3 秒下限影响。不会自动修改历史音频或参考转写。
See the bilingual video guide for formats, limits, expiry, browser storage and detector tradeoffs. The original ASR endpoint remains compatible. New video tasks use the same ASR queue with purpose=video_transcription; global lists/SSE remain summary-only.
POST /api/v1/asr/video-uploadswithIdempotency-Keyand JSON{ "name": "lecture.mp4", "size_bytes": 123456 }returns an upload ID,chunk_bytesand durableoffset(201). Reuse the key after a lost response.PATCH /api/v1/asr/video-uploads/{upload_id}sends a binary chunk withUpload-Offsetand hexadecimal SHA-256Upload-Checksum. Nonfinal chunks must be exactlychunk_bytes. GET returns the ordered checksum manifest; HEAD returns offset/length/expiry headers. Verify local confirmed bytes before resuming.POST /api/v1/asr/video-jobswith its ownIdempotency-Keyand JSON{ "upload_id": "…", "language": "Auto", "compute_device": "gpu" }creates a task (202), or replays the same task (200). Explicit CPU is supported. Optionalvideo_roihas normalizedx,y,width,height. Submission snapshots settings/hotwords; editing a completed task never rewrites the submission. Queue/disk admission uses429plusRetry-After; conflicts use409.- Read
GET /api/v1/jobs/{job_id}/video-scenesfor currentrevisionand illustrated paragraphs. Each scene includesid,start,end,frame_time,image,paragraphs. Get JPEGs at/video-frames/{name}or preview a moment at/video-preview?time=12.5. PATCH /api/v1/jobs/{job_id}/video-scenesaccepts{ "revision": 1, "action": "split", "scene_id": "scene_1", "time": 12.5 }. Actions:merge_next,split,boundary,frame,restore;timeis needed for split/boundary/frame. Restore needs no scene ID. Stale revision returns409; reload and reconcile. Original automatic sections remain inresult.video.original_scenes.GET /api/v1/jobs/{job_id}/transcript.pdfdownloads a concise PDF for any completed ASR job, including historical audio jobs. Video adds images. HEAD prepares/validates the same cached export. A busy renderer returns429. PDF bytes are not persisted as an artifact. Existing audio JSON/TXT/SRT/VTT exports remain available.DELETE /api/v1/asr/video-uploads/{upload_id}immediately discards an unsubmitted upload (204). Submitted uploads follow normal task deletion. Expired uploads return410until swept, then404.
All routes require the normal Bearer/session authentication and same-origin write rules. Strict JSON bodies reject undeclared fields. Upload SHA-256 confirms transfer integrity, not trust in media contents. The executable Python example preserves resumable state across client restarts, including the accepted job ID until a requested PDF is completely saved.
Successful PDF GET responses use application/pdf; frame and preview GET responses use image/jpeg, all binary. PDF HEAD prepares the same export and returns Content-Type, Content-Length and UTF-8 Content-Disposition without a body. Upload HEAD returns Upload-Offset, Upload-Length and Upload-Expires (Unix seconds) without a body. Upload creation/replay returns 201; it is separate from the eight asynchronous submission endpoints. Use the actual returned upload ID in the video-job example, not a literal placeholder.
Scene times are seconds from the shared video origin. ROI coordinates are fractions of the complete picture, with x + width <= 1 and y + height <= 1. frame permits the section start but excludes its end; split requires a time strictly inside that section; boundary requires a time strictly between the current section's start and the following section's end. Merge/boundary need a following section. Capture revision when editing begins and never replace it with a background refresh revision while retaining old inputs. A stale revision returns 409; reload and reconcile before editing again. Swagger includes examples for all five actions.
视频 PDF/JPEG 成功响应为二进制;HEAD 无响应体,上传到期时间单位为 Unix 秒。上传创建/重放的 201 与异步任务首次 202、重放 200 分开。时间均为视频相对秒,ROI 为画面比例。编辑时必须固定开始时的修订号,冲突后重新读取,不得保留旧输入却替换成后台的新 revision。PDF 429 按 Retry-After 重试,413 拆分源文件,401 重新鉴权;下载重试不应重新提交视频任务。
See the bilingual library guide for merge/replace rules, sorting, limits, backup storage and browser preference changes. All library endpoints require the existing Bearer/session authentication when configured.
| Endpoint | Contract |
|---|---|
GET /api/v1/library/export |
Stream the complete ZIP; unfinished enrollment or missing ready audio returns 409 |
POST /api/v1/library/imports |
Multipart file; mandatory 8–128 character Idempotency-Key; first acceptance 202, identical bytes replay 200, conflict/expired 409, admission 429 |
GET /api/v1/library/imports?limit=20 |
Recent persisted receipts (array, limit 1–100) |
GET /api/v1/library/imports/{identifier} |
Poll queued/validating/ready/applying/completed/failed/expired state |
POST /api/v1/library/imports/{identifier}/preview |
Strict JSON {mode: "merge"} or {mode: "replace"}; returns counts and preview revision |
POST /api/v1/library/imports/{identifier}/apply |
Strict JSON mode plus preview_revision; 202, completed matching replay 200; changed/busy library 409 |
GET /api/v1/library/imports/{identifier}/backup |
Protected streaming ZIP of the pre-replacement library; supports Range |
DELETE /api/v1/library/imports/{identifier} |
204; remove inactive staged files, retain completed results/backups; active operation 409 |
completed records the committed database transaction. The manager may still be retiring files and protecting writes, including staging DELETE, with 409 library_maintenance; retry shortly. The completion receipt stays durable for crash recovery. / completed 表示数据库已提交;旧文件清理未完成时,写操作及暂存包删除仍可能返回 409 library_maintenance,应稍后重试,完成回执继续用于崩溃恢复。
A complete executable Python client downloads on A and restores on B. Set client URLs and credentials separately; the key variables are optional for installations without authentication. Install requests in your separate client environment before running this example. LIBRARY_MODE=merge is the default; replace explicitly selects full replacement and requires idle queues. No history is copied or edited. Retry upload errors with the same request_key for the same bytes.
import os
import time
import uuid
from pathlib import Path
import requests
a_url = os.environ["LIBRARY_SOURCE_URL"].rstrip("/")
b_url = os.environ["LIBRARY_TARGET_URL"].rstrip("/")
mode = os.environ.get("LIBRARY_MODE", "merge")
if mode not in {"merge", "replace"}:
raise ValueError("LIBRARY_MODE must be merge or replace")
def session(key_name):
client = requests.Session()
key = os.environ.get(key_name)
if key:
client.headers["Authorization"] = "Bearer " + key
return client
a = session("LIBRARY_SOURCE_KEY")
b = session("LIBRARY_TARGET_KEY")
archive = Path("audio-intel-library.zip")
with a.get(a_url + "/api/v1/library/export", stream=True, timeout=120) as response:
response.raise_for_status()
with archive.open("wb") as output:
for chunk in response.iter_content(256 * 1024):
output.write(chunk)
request_key = uuid.uuid4().hex
with archive.open("rb") as source:
response = b.post(b_url + "/api/v1/library/imports",
headers={"Idempotency-Key": request_key},
files={"file": (archive.name, source, "application/zip")}, timeout=120)
response.raise_for_status()
url = b_url + "/api/v1/library/imports/" + response.json()["id"]
def wait():
deadline = time.monotonic() + 3600
while time.monotonic() < deadline:
response = b.get(url, timeout=30)
response.raise_for_status()
receipt = response.json()
if receipt["state"] not in {"queued", "validating", "applying"}:
if receipt.get("error") or receipt["state"] in {"failed", "expired"}:
raise RuntimeError(receipt.get("error") or receipt["state"])
return receipt
time.sleep(1)
raise TimeoutError("Inspect the persisted import receipt before retrying")
if wait()["state"] != "ready":
raise RuntimeError("Archive is not ready")
response = b.post(url + "/preview", json={"mode": mode}, timeout=120)
response.raise_for_status()
preview = response.json()
print("Preview:", preview)
response = b.post(url + "/apply",
json={"mode": mode, "preview_revision": preview["preview_revision"]}, timeout=120)
response.raise_for_status()
receipt = wait()
if receipt["state"] != "completed":
raise RuntimeError("Import was not applied; inspect receipt and refresh preview")
print("Result:", receipt["result"])
print("Replacement backup:", receipt.get("backup_url"))GET /api/v1/voiceprints/people returns required sort_initial (A–Z or #) and full-pinyin/Latin ordered people; /api/v1/tts/voices uses the same ordering. The frontend passes that order through clone, document TTS, ASR rename/enrollment and video enrollment pickers. System hotword term order and historical speaker snapshots are unchanged.
网页音频和视频表单使用一个“音频增强”总开关,提交 audio_enhancement=auto|off,
统一开启/关闭背景降噪和人声增益;API 也推荐同一总开关,默认效果保持不变;开启后可通过高级参数分别配置力度并恢复默认值。独立参数继续兼容现有 API
客户端及历史任务,网页不再分别提供两项开关。浏览器偏好迁移及暂停上传行为见
增强说明。
The browser audio/video forms use one Audio enhancement checkbox. The API also
recommends the combined control; submit
audio_enhancement=auto|off to enable/disable both operations. Default processing is unchanged; optional numeric strengths configure each operation. Independent API fields and saved request/result contracts remain
supported; paused uploads retain their original settings.
三个入口新增可选 speech_enhancement_strength(有限浮点 0–2,默认 1,网页 0–200%)和
background_denoise_strength(有限浮点 0–1,GTCRN 默认 0.5,网页 0–100%)。
音频/OpenAI 兼容用 multipart,视频用 JSON;非法范围和非有限数返回 422。
能力中两项的 strength: {min,max,step,default,unit} 是范围与默认值的权威来源;实验
UL-UNAS 后端降噪默认 1。增益倍率作用于目标 dB 增益并受原安全限制,降噪值是自动选中
片段的湿声比例,不保证消除全部背景音。调高可能影响自然度或识别率。
Zero skips that operation (bypassed / strength_zero); denoising at zero loads no model.
The master switch off still disables both. New policy-6 requests snapshot both values;
per-operation results optionally include strength. Replay inherits omitted strengths;
changed values return 409. Historical policy ≤5 replay/retry retains its old semantics;
supplying numeric strengths for a historical idempotency record returns 409 and requires a new key.
The UI saves valid changes immediately under preference v7; resetting strengths leaves
other settings intact. Only new submissions use changed values. Paused uploads retain snapshots.
独立布尔参数 speech_enhancement(人声增强)与 background_denoise(背景降噪)
适用于下面三个入口,新任务均默认 true。音频及兼容入口使用 multipart 的 true/false,
视频入口使用 JSON boolean。双开先降噪再平稳增益;仅增益不加载降噪模型。
总开关 audio_enhancement=auto|off 统一开启/关闭两项,与任一独立字段混用返回 422。
省略参数重放时继承已保存设置,改变任一开关返回 409;历史请求不自动升级处理链。
Independent booleans speech_enhancement and background_denoise default to true
on all three endpoints. Both enabled means denoise then continuous gain; gain
alone loads no denoiser. The recommended combined control cannot be mixed with
independent fields (422).
Replay inherits saved settings; changes conflict (409). Historical requests retain
their previous processing path.
Capabilities add asr.audio_enhancement.speech_enhancement and
.background_denoise, each with supported/available/default/strength. Policy-6 detail/results
add the same two objects with enabled/status/reason; denoising also identifies
the actual model/revision/mix. Failure is independent: status=applied with
reason=partial_fallback can mean gain succeeded while denoising failed.
Global job lists and SSE remain summary-only. The validated GTCRN mix remains the
default; the stronger UL-UNAS candidate failed public CER validation. See the
enhancement guide.
BASE_URL="${AUDIO_INTEL_BASE_URL:-http://127.0.0.1:20810}"
API_KEY="${AUDIO_INTEL_API_KEY:?Set AUDIO_INTEL_API_KEY}"
# Recommended: combined automatic denoising and speech gain.
curl --fail-with-body -H "Authorization: Bearer $API_KEY" -H "Idempotency-Key: combined-audio-001" \
-F file=@meeting.wav -F audio_enhancement=auto \
-F speech_enhancement_strength=1.5 -F background_denoise_strength=0.75 "$BASE_URL/api/v1/asr/jobs"
# Compatibility example: independent gain without denoising; do not mix fields.
curl -H "Authorization: Bearer $API_KEY" -H "Idempotency-Key: independent-gain-001" \
-F file=@meeting.wav -F speech_enhancement=true -F background_denoise=false \
"$BASE_URL/api/v1/asr/jobs"新音频、视频及 OpenAI 兼容转写默认 audio_enhancement=auto,显式 off 关闭。
增强使用本地 CPU FP32,与 ASR 的 CPU/GPU 选择独立;保留全部交谈者,干净且音量
充足的片段旁路。声纹导入和克隆参考分析不启用增强。
New audio, video and compatible transcription requests default to auto; off
opts out. Enhancement runs locally on CPU FP32 independently of ASR device choice,
preserves conversation speakers, and bypasses clean audible blocks. Voiceprint
imports and clone-reference analysis are excluded.
| Endpoint | Input / 输入 |
|---|---|
POST /api/v1/asr/jobs |
multipart audio_enhancement=auto|off |
POST /api/v1/asr/video-jobs |
JSON audio_enhancement: "auto" or "off" |
POST /v1/audio/transcriptions |
multipart audio_enhancement=auto|off |
GET /api/v1/capabilities 的 asr.audio_enhancement 返回 supported、available、
default、modes、固定模型及 revision、compute_device=cpu 和回放能力。
available 反映所选降噪模型是否就绪;两项独立能力分别反映增益和降噪可用性。
available=false 不禁用基础 ASR 或独立增益。当前策略 6 分别尝试两项处理,降噪
失败时增益仍可成功;总状态可能为 applied、原因为 partial_fallback。非法模式返回 422。
Capabilities expose support, availability, default/modes, pinned model
identity/revision, CPU execution and enhanced-playback support. Top-level availability
reflects the denoiser; per-operation capabilities report gain separately. Missing
denoising weights leave base ASR and gain available. Policies 5 and 6 report independent
outcomes; partial success is applied with partial_fallback. Invalid modes return 422.
Result status |
Meaning / 含义 |
|---|---|
off |
Saved request disables enhancement / 保存的请求关闭增强 |
bypassed |
No processing needed; original used / 无需增强,使用原音 |
applied |
Enhanced input for transcription/alignment / 转写及对齐使用增强音 |
fallback |
No usable enhanced output; reason recorded, original used / 未生成可用增强音,记录原因后使用原音 |
详情及结果中的 audio_enhancement 可选;缺失的历史结果不表示增强成功。
客户端应保留对未知 reason 的兼容。实际增强时返回 playback_url 与可选 waveform,
并声明独立 enhanced.wav 产物。该 URL 使用普通 Bearer/浏览器会话鉴权,支持
Range;追加 /waveform 获取波形。原 /playback 和源文件语义不变,纯旁路不存
额外 WAV。全局任务列表及 SSE 不包含增强详情或媒体结果。
Job detail/results optionally include audio_enhancement; absence in history
does not imply processing. Tolerate future reason strings. Only applied results
offer a separate authenticated Range-capable enhanced artifact and optional peaks;
append /waveform to its URL to fetch peaks lazily. Original playback/source
semantics remain, bypass retains no extra WAV, and global summaries/SSE omit detail.
max_gain_db is gain in dB; input_samples is preserved sample count.
gain_seconds/denoised_seconds cover processing blocks, not exact voiced duration.
elapsed_seconds measures enhancement, excluding subsequent VAD and other stages.
当前策略 6 保留策略 4 引入的人声响度目标、50% GTCRN 混合及连续增益; 使用 6 秒人声上下文与跨块连续增益, 最大变化每秒 3 dB,避免逐秒增益判断带动背景声起落。两次人声检测的并集仍用于 ASR 分段。 新结果可包含下列可选字段,旧任务缺失时不表示已执行新策略:
| Field / 字段 | Meaning / 含义 |
|---|---|
processing_version |
Signal policy version, independent of model revision / 信号处理策略版本,与模型 revision 独立 |
pause_attenuated_seconds |
Seconds actually attenuated, not exact non-speech duration / 实际衰减秒数,不是精确非语音时长 |
max_pause_attenuation_db |
Maximum applied reduction, positive dB / 实际最大衰减量,正数 dB |
pause_suppression_reason |
Applied/skipped reason; tolerate future values / 执行或跳过原因,客户端应兼容未知值 |
Current policy 6 retains policy 4's continuous gain, loudness target and
50% GTCRN mix, preserving sample count and media clock. Six-second speech context and peak-safe continuous gain bounded to 3 dB/s
replace per-second gain decisions. Policies 4 and 5 report zero for both pause
attenuation fields and disabled_for_naturalness. Historical policy-2/3 diagnostics remain
valid, including applied, no_speech, no_noise, no_long_pauses,
vad_unavailable, and bypassed. A second-VAD failure retains base enhancement
and original speech regions. elapsed_seconds includes signal processing and
excludes VAD. The combined field accepts only auto|off; compatible independent
fields remain available. Enhancement progress in media_second accumulates work
across passes and must not be read as recording duration; use result.duration.
同一幂等键改变增强模式返回 409。重放旧请求时省略字段仍返回原任务,旧请求没有
该字段按 off 解释;失败/取消重试保留保存的模式。历史请求、结果和媒体不自动改写。
Changing mode with the same idempotency key returns 409. Omitting the field when
replaying a historical request preserves its original off semantics. Retry uses
the persisted mode; historical requests/results/media are never backfilled.
BASE_URL=${AUDIO_INTEL_BASE_URL:-http://127.0.0.1:20810}
AUTH=(); if [[ -n "${AUDIO_INTEL_API_KEY:-}" ]]; then AUTH=(-H "Authorization: Bearer $AUDIO_INTEL_API_KEY"); fi
REQUEST_KEY=$(python3 -c 'import uuid; print(uuid.uuid4())')
# Native asynchronous ASR, explicit automatic enhancement.
curl --fail-with-body -sS "${AUTH[@]}" -H "Idempotency-Key: $REQUEST_KEY" \
-F file=@meeting.wav -F compute_device=cpu -F audio_enhancement=auto "$BASE_URL/api/v1/asr/jobs"
# Compatible synchronous transcription, explicit opt-out.
curl --fail-with-body -sS "${AUTH[@]}" -F file=@meeting.wav \
-F model=qwen3-asr-0.6b -F compute_device=cpu -F audio_enhancement=off "$BASE_URL/v1/audio/transcriptions"
# Use an upload ID from the completed video-upload workflow described above.
VIDEO_UPLOAD_ID=${VIDEO_UPLOAD_ID:?Set a completed video upload ID}
VIDEO_REQUEST_KEY=$(python3 -c 'import uuid; print(uuid.uuid4())')
jq -n --arg upload_id "$VIDEO_UPLOAD_ID" \
'{upload_id:$upload_id,compute_device:"cpu",audio_enhancement:"auto"}' | \
curl --fail-with-body -sS "${AUTH[@]}" -H "Idempotency-Key: $VIDEO_REQUEST_KEY" \
-H 'Content-Type: application/json' --data-binary @- "$BASE_URL/api/v1/asr/video-jobs"See the processing and validation contract. Background speakers and severe reverberation can remain; text agreement, listening level and non-speech energy changes do not establish accuracy without a trusted transcript.