Skip to content

Releases

Laras v0.2.1 — jalan di arm64, dan bisa dicoba tanpa curl

Choose a tag to compare

@Urdemonlord Urdemonlord released this 10 Nov 11:29

Laras v0.2.1 — jalan di arm64, dan bisa dicoba tanpa curl

Dua celah adopsi yang tersisa di v0.2.0 ditutup: image-nya sekarang multi-arch,
dan server punya halaman depan sehingga orang bisa mendengar suaranya tanpa
membaca README dulu.

Image untuk amd64 dan arm64

Satu manifest, dua arsitektur. Mac M-series, Graviton, dan Ampere menarik varian
yang benar tanpa --platform:

docker run --rm -p 8080:8080 -v "$PWD/data:/data" \
  ghcr.io/urdemonlord/laras-tts:0.2.1

Keduanya dibangun di runner native masing-masing (ubuntu-latest dan
ubuntu-24.04-arm) lalu manifest-nya digabung dengan buildx imagetools create — bukan QEMU, karena emulasi build Rust sebesar ini 5–10× lebih lambat.
CI memverifikasi manifest benar-benar berisi linux/amd64 dan linux/arm64
sebelum image dinyatakan terbit.

GET / — halaman depan

Satu kotak teks, satu tombol putar, dan perintah curl yang setara. Berkasnya
demo/index.html, di-include_str! ke dalam binary, jadi tetap satu berkas
tanpa direktori aset. Halaman ini bebas token dan bebas batas laju seperti
/health: isinya statis dan tidak memakai CPU. Sintesis tetap butuh token
kalau LARAS_TOKEN diisi.

Demo publik dalam satu perintah

pip install -U "huggingface_hub[cli]"
hf auth login
scripts/terbitkan-space.sh --user <akun-hf>

Skrip itu membuat HF Space (SDK docker), menyalin bundel demo/hf-space/,
push, lalu menunggu sampai statusnya RUNNING. Token tidak pernah masuk ke URL
git atau argv — ditulis ke berkas 600 lalu dibaca credential helper.

Isi bundelnya:

  • model diunduh saat build, bukan saat start — pengunjung pertama tidak
    menunggu 160 MB;
  • unduhannya memakai scripts/unduh-model.sh yang dipin ke tag rilis, jadi
    sha256-nya ikut diverifikasi;
  • referensinya sampel Common Voice CC0, bukan rekaman pribadi siapa pun;
  • TAKES=1, RATE_LIMIT=6 karena CPU Space dipakai bersama.

Perbaikan lain

  • scripts/uji-image.sh — smoke test image dipindah dari YAML ke skrip, dipakai
    jalur rilis dan jalur PR, jadi yang diuji adalah artefak yang benar-benar
    di-push (arch, ldd, glibc, ffmpeg mp3→wav, gagal-muat-model).
  • Dockerfile menyalin demo/index.html ke tahap build (tanpa itu
    include_str! gagal).
  • CI menolak skrip yang sintaksnya rusak dan --cek yang menerima direktori
    model kosong.
  • Versi crate 0.2.1, /health ikut melaporkan 0.2.1.

Yang masih batas model, bukan bug

Artikulasi gugus konsonan ("kloning" → "koning") tidak diperbaiki di rilis ini.
Akarnya kosakata tokenizer 4.000 kata; memperbaikinya butuh latih ulang dengan
kosakata 8–16 ribu dan itu butuh GPU. Rilis ini menutup celah adopsi, bukan
celah kualitas — dan itu disebut apa adanya di README.

Uji

  • 40 tes cargo test --features onnx, 26 tanpa fitur; cargo fmt --check dan clippy -D warnings bersih di kedua kombinasi.
  • CI 5/5 hijau pada commit yang di-tag.
  • Manifest diverifikasi lewat registry API: linux/amd64 + linux/arm64.

Full Changelog: v0.2.0...v0.2.1

Laras v0.2.0 — pengaman runtime dan tes HTTP

Choose a tag to compare

@Urdemonlord Urdemonlord released this 10 Nov 10:19

Port Laras sebelumnya terbuka: siapa pun yang bisa menjangkau port-nya boleh
memakai CPU, dan permintaan bisa menumpuk sampai memori habis. Rilis ini
menutup itu — dan menambah tes HTTP yang selama ini tidak ada.

Pengaman runtime

Semuanya opsional dan mati secara default, jadi localhost tetap tanpa gesekan.

Variabel Default Arti
LARAS_TOKEN kosong (terbuka) semua rute kecuali /health butuh Authorization: Bearer <token>
RATE_LIMIT 0 (mati) permintaan per menit per klien
MAX_CONCURRENT 1 slot sintesis paralel
QUEUE_WAIT 120 detik menunggu slot sebelum 429
REQUEST_TIMEOUT 300 detik sampai header respons siap sebelum 504
MAX_BODY_KB 64 batas body permintaan sebelum 413
LARAS_TOKEN=$(openssl rand -hex 24) BIND=0.0.0.0 ./target/release/laras
curl -H "Authorization: Bearer $LARAS_TOKEN" \
     -d '{"text":"Halo, apa kabar?"}' localhost:8080/tts -o halo.wav

Detail yang disengaja:

  • /health bebas token dan bebas batas laju — healthcheck container tidak
    boleh ikut terkunci.
  • Token dibandingkan dengan waktu konstan, jadi panjang dan isinya tidak
    bocor lewat timing.
  • Cache hit tidak ikut mengantre. Permintaan yang hasilnya sudah ada
    dijawab langsung; yang mengantre hanya kerja sintesis.
  • Antrean penuh dijawab 429 + Retry-After, bukan digantung sampai klien
    menyerah.
  • Server mencetak peringatan di log kalau BIND bukan loopback sementara
    LARAS_TOKEN kosong.

Tes HTTP

39 tes dengan --features onnx (26 tanpa fitur itu). Rute diuji lewat
Service::oneshot: axum sungguhan, tanpa membuka port, dan memakai
bangun_router() yang sama dengan main — jadi urutan layer-nya ikut teruji,
bukan cuma handler-nya.

Yang diuji: /health tetap jalan tanpa token, 401 untuk token salah,
400 untuk teks kosong, 413 untuk body kebesaran, 429 + Retry-After saat
jatah laju habis, 504 saat handler terlalu lambat, dan pemulihan antrean
setelah slot dilepas.

Untuk bisa menguji rute tanpa memuat model 438 MB, ada varian Backend::Uji
yang hanya hidup saat cfg(test).

Diukur, bukan diasumsikan

Dengan MAX_CONCURRENT=1, QUEUE_WAIT=2, RATE_LIMIT=3, satu sintesis
5 kalimat berjalan (7,77 s):

Permintaan Hasil
Teks yang sudah ada di cache 200 dalam 0,0008 s, x-cache: hit
Teks baru saat slot terpakai 429 setelah 2,00 s, Retry-After: 5
Sintesis panjang 200, 439.724 byte

Batas lain yang terverifikasi: 401 + WWW-Authenticate: Bearer, 413 untuk
body 3 KB saat batas 2 KB, dan 429 dengan Retry-After: 45 saat jatah menit
habis.

Perbaikan kecil

  • Versi crate naik ke 0.2.0, jadi /health akhirnya melaporkan versi yang
    sama dengan tag rilis (sebelumnya image 0.1.1 masih bilang 0.1.0).
  • README: bagian "Pengaman runtime" + tabel jawaban 401/429/413/504,
    bagian "Kualitas", dan satu batasan usang ("belum ada streaming") dibuang.

Image: ghcr.io/urdemonlord/laras-tts:0.2.0 (juga 0.2, latest).

Laras v0.1.1 — unduh model sekali jalan, referensi MP3 langsung dipakai

Choose a tag to compare

@Urdemonlord Urdemonlord released this 10 Nov 08:15

Rilis patch yang menutup dua gesekan yang bikin orang harus menebak-nebak saat
pertama kali mencoba Laras.

1. Model tidak perlu diunduh manual lagi

scripts/unduh-model.sh mengunduh 9 berkas dari Hugging Face dan memverifikasi
sha256 setiap berkas. Hash tidak cocok = berhenti dengan kode keluar 1,
bukan diam-diam memakai berkas rusak atau setengah jadi.

scripts/unduh-model.sh          # ±160 MB: model + contoh referensi CC0
scripts/unduh-model.sh --cek    # hanya verifikasi yang sudah ada
scripts/unduh-model.sh --dir /data/models --ref-dir /data/ref

Repo bobotnya publik — tidak perlu huggingface-cli login. (Bobot asli
kyutai/pocket-tts gated; itu hanya relevan kalau kamu mau mengekspor graf
ONNX sendiri.)

2. Referensi MP3 langsung jalan

Sebelumnya REF_WAV hanya menerima WAV, padahal contoh referensi Common Voice
berformat MP3 — jadi permintaan pertama gagal dengan pesan "hanya WAV yang
didukung". Sekarang WAV, MP3, m4a, ogg, flac semuanya diterima: selain WAV
dikonversi otomatis ke WAV 24 kHz mono lewat ffmpeg, dan hasilnya dipakai
ulang selama berkas sumbernya tidak berubah (tidak ada konversi berulang di
setiap permintaan).

Kalau ffmpeg tidak terpasang, galatnya menyebut perintah konversi manual —
bukan menyuruh menebak.

Image rilis sekarang memasang ffmpeg, jadi perilaku ini sama di dalam
container.

Perbaikan lain

  • Peringatan saat start kalau REF_WAV tidak ada, bukan gagal di permintaan pertama.
  • docker compose up memakai image ghcr (bukan build: .), plus make models dan make cek-model.
  • README: bagian "Menyiapkan model", format referensi yang didukung, dan diagram arsitektur yang sesuai kenyataan — ONNX native adalah default, Python hanya jalur cadangan.
  • Smoke test CI memeriksa ffmpeg di dalam image dan benar-benar mengonversi satu MP3; CI juga memastikan --cek menolak direktori kosong dengan kode 1.

Bukti

  • Referensi suara.mp3 (180 KB, 15 detik) dipakai langsung: sintesis 2,80 detik
    audio berhasil tanpa konversi manual, dan permintaan berikutnya memakai hasil
    konversi yang sama (mtime tidak berubah, tidak ada berkas baru).
  • 28 tes lulus dengan --features onnx, termasuk 4 tes baru untuk pembacaan
    audio dan pemakaian ulang konversi.
  • CI main hijau 5/5; image diuji dari artefak yang benar-benar di-push.

Image: ghcr.io/urdemonlord/laras-tts:0.1.1 (juga 0.1, latest).

Laras v0.1.0 — TTS Bahasa Indonesia, CPU-first

Choose a tag to compare

@Urdemonlord Urdemonlord released this 10 Nov 00:45

Rilis pertama Laras: engine TTS Bahasa Indonesia yang CPU-first, self-hosted, dengan
kloning suara. Satu binary Rust, ONNX Runtime native (tanpa Python di jalur
permintaan), dan audio yang dikirim per kalimat.

Yang bisa dipakai

  • POST /tts — WAV utuh dari teks, lengkap dengan normalisasi angka/rupiah/persen
    dan pemilihan take adaptif.
  • POST /tts/stream — audio dikirim per kalimat begitu selesai disintesis;
    ?fmt=pcm untuk browser dan pemutar yang menolak WAV tanpa panjang pasti.
  • GET /health, GET /normalize — cek status, dan debug normalizer tanpa memuat model.
  • Kloning suara dari 10–15 detik rekaman referensi (REF_WAV).

Angka terukur, bukan estimasi

Teks 8 kalimat (22,7 s audio), 4 vCPU, cache kosong:

Momen /tts /tts/stream
byte pertama 3,76 s 0,26 s
50% audio – 2,04 s
selesai 3,76 s 3,85 s

Keluaran identik: 1.091.084 byte di kedua jalur, PCM hasil decode sama
bit-per-bit. Latensi dengar turun ±14× dengan tambahan total 0,09 s.

Image Docker

docker run --rm -p 8080:8080 \
  -v "$PWD/data:/data" \
  ghcr.io/urdemonlord/laras-tts:0.1.0

linux/amd64, basis debian:trixie-slim, satu binary, ONNX Runtime ter-link
statis (tidak ada libonnxruntime.so di ldd). Butuh /data/models (graf ONNX)
dan /data/ref/suara.wav; tanpa itu proses berhenti dengan gagal memuat model ONNX.

Image diuji di CI sebelum dipakai: ldd tanpa "not found", binary menuntut
GLIBC_2.38 sementara runtime menyediakan 2.41, dan binary terbukti sampai
tahap memuat model di dalam container.

Batasan yang sudah diukur

  • Teks ≤20 kata tidak dapat manfaat streaming: 4 kata tetap 8,83 s di kedua jalur.
  • Header WAV memakai ukuran 0xFFFFFFFF karena total belum diketahui saat header
    dikirim; ffmpeg memberi peringatan di EOF, tapi decode tetap identik.
  • Normalisasi loudness dilakukan per potongan pada jalur streaming (RMS −17,8 dBFS
    vs −17,3 dBFS di /tts), dan /tts/stream tidak menulis cache.
  • MSRV 1.92, dipaksa ort 2.0.0-rc.15. Image memakai trixie karena binary
    menuntut glibc ≥ 2.38 — bookworm tidak bisa dipakai sama sekali.

Catatan teknis

  • Pemilihan take adaptif berbasis ASR: rata-rata 10,8 s dari sebelumnya 14,5 s,
    kualitas setara (WER 0,168 vs 0,163; 27 dari 28 kalimat identik).
  • Normalisasi loudness BS.1770 + limiter puncak.
  • Image dibangun dan diuji di GitHub Actions; tidak ada build Docker di mesin
    produksi.

Lisensi: MIT OR Apache-2.0. Konfigurasi lengkap ada di README.

Laras Desktop v0.1.0 — Windows x86_64

Choose a tag to compare

@Urdemonlord Urdemonlord released this 10 Nov 22:59

Pratinjau pertama aplikasi desktop Laras untuk Windows.

Isi paket (ekstrak semua ke satu folder):

  • laras-desktop.exe
  • DirectML.dll
  • data/models/ (9 berkas ONNX, 157 MB)
  • data/ref/suara.mp3 (contoh Common Voice CC0)
  • BACA-DULU.txt

Cara pakai: ekstrak, lalu dobel-klik laras-desktop.exe. Tanpa jaringan, tanpa GPU.

Catatan: exe belum ditandatangani, jadi SmartScreen akan memperingatkan (More info -> Run anyway). Jalur sintesis di Windows belum diuji otomatis; laporan bug sangat membantu.