Hướng dẫn tích hợp API
Kokoro Vietnamese cho TTS, PhoWhisper-base cho STT. Các ví dụ HTTP dùng địa chỉ của dịch vụ đang mở.
Endpoint
| Phương thức và đường dẫn | Kết quả |
|---|---|
| GET /health | 200: status, version, queue_depth, worker_busy, free_disk_mb. |
| GET /v1/models | 200: tts/stt, trạng thái và voices thật. Chọn voice.id từ danh mục. |
| GET /v1/runtime | 200: snapshot CPU/RAM, effective_threads, heavy_task_concurrency, queue_limit. |
| POST /v1/tts/jobs | JSON → 202: job_id, reused, job. |
| POST /v1/stt/jobs | Multipart trường file → 202: job_id, reused, job. |
| GET /v1/jobs/{id} | 200: job cùng tiến độ; TTS có chunks/audio_url, STT có text khi hoàn tất. |
| POST /v1/jobs/{id}/cancel | 200: job_id, status. Hủy đang chạy có thể chờ lượt suy luận hiện tại. |
| GET /v1/jobs/{id}/chunks/{index} | 200: audio/wav. Index bắt đầu từ 0; chưa sẵn sàng hoặc hết hạn trả 404. |
| GET /v1/jobs/{id}/audio | 200: một WAV ghép đầy đủ khi completed; chưa sẵn sàng/hết hạn trả 404. |
| WS /v1/stt/stream | Nhận ready, gửi PCM16 LE mono 16kHz, nhận partial/final/end. |
Tạo giọng đọc
Đầu vào JSON: text bắt buộc; model mặc định kokoro-vi, voice mặc định diem_trinh, speed mặc định 1.0 (0.25–4.0). Văn bản dài được chia tự động. Các URL trả về là đường dẫn tương đối trên cùng dịch vụ.
curl -X POST "http://127.0.0.1:6800/v1/tts/jobs" \
-H "Content-Type: application/json" \
-d '{"text":"Xin chào. Đại hội XIV.","model":"kokoro-vi","voice":"diem_trinh","speed":1.0}'
{"job_id":"job_...","reused":false,"job":{"id":"job_...","type":"tts","status":"queued","progress":{"done":0,"total":1,"percent":0,"stage":"queued","unit":"chunks","current_chunk":null,"elapsed_seconds":0},"cancel_requested":false,"normalization_version":"vi-2"}}
Đối với tên riêng, thêm pronunciation_overrides, ví dụ {"OpenAI":"ô pần ây ai"}. Tối đa 32 mục, mỗi cụm 80 ký tự, cách đọc 200 ký tự; literal, phân biệt hoa/thường, ưu tiên cụm dài nhất. Chuẩn hóa hỗ trợ số/ngày/giờ/đơn vị và số La Mã theo ngữ cảnh; không bảo đảm phát âm mọi tên riêng.
Theo dõi và lấy kết quả
Poll mỗi khoảng một giây. Khi completed, đọc text STT hoặc tải audio_url TTS. Dừng poll ở failed/cancelled/interrupted và hiển thị error nếu có. Completed không có nghĩa model nhận đúng mọi từ.
{"done":1,"total":3,"percent":33.3,"stage":"synthesizing","unit":"chunks","current_chunk":2,"elapsed_seconds":12.5}
Đây là giá trị job.progress. Phần trăm TTS dựa trên số đoạn hoàn thành, giữ tối đa 99 trước khi ghép xong; completed là 100. STT trả percent=null trong lúc xử lý, nên dùng thanh chờ. Stage có thể là queued, waiting_for_slot, loading_model, synthesizing, merging, transcribing, cancelling hoặc trạng thái cuối.
Header tùy chọn Idempotency-Key: cùng endpoint + cùng giá trị + cùng payload trả lại job cũ; payload khác trả 409. Dùng UUID riêng mỗi tác vụ, giữ UUID khi retry; các ứng dụng dùng chung namespace. Không dùng một giá trị cố định cho mọi yêu cầu.
Python: TTS và tải một WAV đầy đủ
import time
import requests
BASE = "http://127.0.0.1:6800"
def wait_job(job_id):
deadline = time.monotonic() + 600
while time.monotonic() < deadline:
r = requests.get(f"{BASE}/v1/jobs/{job_id}", timeout=10)
r.raise_for_status()
job = r.json()
if job["status"] == "completed":
return job
if job["status"] in {"failed", "cancelled", "interrupted"}:
raise RuntimeError(job)
time.sleep(1)
raise TimeoutError(job_id)
r = requests.post(BASE + "/v1/tts/jobs", json={
"text": "Xin chào.", "voice": "diem_trinh"
}, timeout=30)
r.raise_for_status()
job = wait_job(r.json()["job_id"])
r = requests.get(BASE + job["audio_url"], timeout=60)
r.raise_for_status()
with open("out.wav", "wb") as f:
f.write(r.content)
Nhận dạng file âm thanh
Multipart trường file, tối đa 25 MiB và 300 giây. Nhận WAV/MP3/M4A/WebM cùng các định dạng PyAV giải mã được. Không tự đặt Content-Type multipart; thư viện cần tự tạo boundary.
curl -X POST "http://127.0.0.1:6800/v1/stt/jobs" -F "file=@recording.wav"
Python, dùng hàm wait_job ở trên:
with open("recording.wav", "rb") as f:
r = requests.post(BASE + "/v1/stt/jobs", files={"file": f}, timeout=60)
r.raise_for_status()
job = wait_job(r.json()["job_id"])
print(job["text"])
Vùng có lời nói được giải mã riêng, tối đa 20 giây mỗi lượt. VAD giảm văn bản tự sinh trên im lặng; tiếng nhạc, giọng chồng lấn hoặc môi trường ồn vẫn có thể gây nhận sai. Không tự thêm từ theo văn bản dự đoán.
STT trực tiếp qua WebSocket
- Mở WS/WSS cùng dịch vụ, đợi
{"type":"ready","sample_rate":16000,"format":"pcm16le"}. - Gửi từng frame PCM16 little-endian, mono, 16kHz; tối đa 65536 bytes/frame, số byte chẵn. Không gửi header WAV hay blob MediaRecorder WebM.
- Nhận partial (tạm thời) và final (kết quả cuối). Mỗi text là toàn bộ nội dung hiện tại; thay nội dung hiển thị, không nối mọi partial lại.
- Gửi
{"type":"stop"}rồi tiếp tục đợi final và end trước khi đóng. Nếu final.complete=false hoặc có error, không coi đó là phiên thành công đầy đủ.
const url = new URL("/v1/stt/stream", location.origin);
url.protocol = location.protocol === "https:" ? "wss:" : "ws:";
const ws = new WebSocket(url);
let ready = false;
ws.onmessage = ({data}) => {
const message = JSON.parse(data);
if (message.type === "ready") ready = true;
if (message.type === "partial" || message.type === "final") {
console.log(message.text);
}
if (message.type === "error") console.error(message.code, message.message);
if (message.type === "end") { ready = false; ws.close(); }
};
function sendPCM16(arrayBuffer) {
if (ready && ws.readyState === WebSocket.OPEN) ws.send(arrayBuffer);
}
function stop() {
if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({type:"stop"}));
}
Ví dụ JavaScript chạy trên cùng origin dịch vụ. Demo đã xử lý thu micro và resample; HTTPS cần WSS. Giới hạn phiên 300 giây, idle không có audio 30 giây; không gửi audio nhanh hơn realtime quá 15 giây. Khi slot đang bận, server trả error/service_busy rồi đóng phiên. Kiểm tra origin bổ sung tại Settings.allowed_origins nếu dùng giao diện ở domain khác.
Giới hạn và xử lý lỗi
Một tác vụ suy luận nặng tại một thời điểm, hàng đợi tối đa 5 job. Phiên live giữ slot suốt phiên. TTS ≤50000 ký tự, JSON ≤1 MiB, kết quả giữ 24 giờ. Chờ có khoảng nghỉ khi retry queue đầy.
{"error":{"code":"queue_full","message":"Job queue is full, retry later","request_id":"..."}}
| Code | HTTP / nơi trả | Xử lý |
|---|---|---|
| invalid_request | 400/422 | Kiểm tra dữ liệu và trường bắt buộc. |
| not_found | 404 | Kiểm tra job ID, tiến độ và TTL audio. |
| conflict | 409 | Idempotency-Key đã dùng cho payload khác. |
| payload_too_large | 413 | Giảm kích thước đầu vào. |
| unprocessable_media | 422 | Kiểm tra định dạng, thời lượng file. |
| queue_full | 429 | Đợi rồi retry cùng tác vụ. |
| service_busy / model_not_ready | 503 hoặc WS error | Đợi slot hoặc kiểm tra model trên máy chủ. |
| processing_error | error trong job/WS | Ghi job ID và thông tin lỗi để kiểm tra. |
Mặc định tối đa 2 luồng; sửa inference_threads trong app/config.py để cho phép 4/8, rồi khởi động lại. Dịch vụ tự giới hạn thêm theo CPU/quota đã cấp; cấu hình tài nguyên được đọc một lần lúc startup.