ID ▾

API teks tanpa sensor untuk pipa suara

uncensoredhttps://api.xtts.cc/v1

API TTS Python: Siapkan Teks untuk Suara dengan SDK

API ini tidak mengembalikan audio. Ini adalah model teks yang Anda panggil dari Python dengan SDK OpenAI, dan cocok untuk diletakkan di depan mesin suara: ini menulis skrip, membersihkannya, dan membaginya menjadi kalimat yang dapat dibaca oleh tahap suara Anda.

Diperbarui

Ilustrasi isometrik blok teks mengalir dari terminal ke gerbang gelombang suara

Apa yang dilakukan dan tidak dilakukan API ini

Jika Anda mencari tutorial tts api python, satu hal perlu jelas sebelum kode apa pun: layanan ini tidak mensintesis suara. Layanan ini menyediakan POST /v1/chat/completions dan GET /v1/models untuk satu model teks bobot terbuka, dengan ID model uncensored. Tidak ada endpoint audio, daftar suara, atau file audio dalam respons apa pun. Layanan ini juga tidak memiliki output gambar, video, atau embedding.

Jadi, di mana letaknya dalam proyek suara? Di bagian teks dari pipeline. Sebelum mesin suara dapat membaca skrip, seseorang harus menulisnya, memperluas angka dan singkatan menjadi kata-kata yang diucapkan, menghapus markup, dan memotongnya menjadi bagian yang cukup pendek untuk disintesis tanpa jeda panjang. Model bahasa melakukan pekerjaan itu dengan baik, dan Anda dapat memanggilnya dari Python dengan SDK yang sama yang mungkin sudah Anda gunakan untuk layanan kompatibel OpenAI lainnya.

Model ini di-tune untuk menjawab tanpa penolakan konten untuk penggunaan dewasa yang sah, yang penting untuk fiksi, dialog karakter, dan narasi di mana model yang lebih ketat mungkin berhenti di tengah adegan. Satu aturan keras berlaku: tidak ada konten seksual yang melibatkan anak di bawah umur.

Sisa artikel ini membangun modul Python kecil dalam empat langkah: panggilan pertama, pemisah kalimat streaming, penormalisasi teks dengan output JSON, dan pembungkus percobaan ulang yang menghormati batas laju.

Instal SDK dan buat panggilan pertama

Anda memerlukan Python 3.8 atau yang lebih baru dan paket resmi openai. Buat kunci di halaman kunci API terlebih dahulu. Akun baru dapat mengklaim kredit uji coba gratis senilai $0,50 di sana dengan satu klik, dan tidak perlu kartu untuk itu.

pip install openai

Jauhkan kunci dari sumber Anda. Ekspor sebagai variabel lingkungan dan baca dalam kode:

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.xtts.cc/v1",
    api_key=os.environ["XTTS_API_KEY"],
)

resp = client.chat.completions.create(
    model="uncensored",
    messages=[
        {"role": "system", "content": "You write short narration scripts. Plain sentences only."},
        {"role": "user", "content": "Write a 60-word intro for a podcast about night trains."},
    ],
    temperature=0.8,
)

script = resp.choices[0].message.content
print(script)
print(resp.usage.prompt_tokens, resp.usage.completion_tokens)

Itulah seluruh integrasi. SDK mengirimkan Authorization: Bearer <key> untuk Anda. Setiap respons juga menyertakan header X-Balance-USD, sehingga Anda dapat membaca sisa kredit Anda dari respons mentah jika ingin mencatatnya. Halaman dokumentasi mencantumkan header dan parameter lainnya.

Parameter seperti temperature, top_p, stop, seed dan penalti diteruskan. Untuk narasi, temperature antara 0,6 dan 0,9 adalah rentang awal yang masuk akal, dan seed yang tetap membantu ketika Anda ingin mereproduksi pengambilan saat menyetel prompt.

Stream skrip dan potong menjadi kalimat

Mesin suara biasanya bekerja kalimat demi kalimat. Jika Anda menunggu skrip 900 kata selesai, pendengar juga menunggu. Dengan stream: true API mengirimkan token melalui event yang dikirim server, dan Anda dapat menyerahkan setiap kalimat yang selesai ke tahap suara sementara sisanya masih ditulis.

import os
import re
from openai import OpenAI

client = OpenAI(base_url="https://api.xtts.cc/v1", api_key=os.environ["XTTS_API_KEY"])
SENTENCE_END = re.compile(r"(?<=[.?])\s+")

def stream_sentences(topic):
    stream = client.chat.completions.create(
        model="uncensored",
        messages=[
            {"role": "system", "content": "Write spoken narration. One idea per sentence."},
            {"role": "user", "content": topic},
        ],
        stream=True,
    )
    buffer = ""
    for chunk in stream:
        if not chunk.choices:
            continue  # the final chunk can carry usage only
        buffer += chunk.choices[0].delta.content or ""
        parts = SENTENCE_END.split(buffer)
        for sentence in parts[:-1]:
            yield sentence.strip()
        buffer = parts[-1]
    if buffer.strip():
        yield buffer.strip()

for line in stream_sentences("Explain how a tide table is read."):
    print(line)  # replace with: your_voice_engine.say(line)

Dua hal mudah terlewatkan. Pertama, angka penggunaan tiba di akhir streaming, dalam chunk yang mungkin memiliki daftar choices kosong, itulah mengapa loop melewati chunk tanpa choices. Kedua, permintaan streaming memegang slot konkurensinya hingga streaming berakhir. Jika mesin suara Anda lambat dan Anda membaca streaming secara malas, slot tetap sibuk. Ambil teksnya dengan cepat, masukkan kalimat ke dalam antrian, dan biarkan sisi audio mengonsumsi antrian itu sesuai kecepatannya sendiri.

Ekspresi reguler di atas memisahkan berdasarkan titik atau tanda tanya diikuti spasi. Ini akan salah memisahkan setelah singkatan seperti "Dr." atau "No.", yang merupakan alasan lain untuk menormalisasi teks terlebih dahulu, seperti yang ditunjukkan bagian berikutnya.

Normalisasi teks untuk telinga

Teks tertulis dan teks lisan berbeda. "Dr. Lee membeli 3 salinan pada 3/4 untuk edisi ke-2" mudah dibaca dan canggung diucapkan. Banyak mesin teks-ke-suara menangani sebagian dari ini, tetapi hasilnya bervariasi tergantung mesin, sehingga membersihkan skrip sebelum mencapai tahap suara menghilangkan kelas kejutan. Minta model untuk melakukannya dengan instruksi eksplisit:

import os
from openai import OpenAI

client = OpenAI(base_url="https://api.xtts.cc/v1", api_key=os.environ["XTTS_API_KEY"])

NORMALIZE = (
    "Rewrite the user's text so it can be read aloud. "
    "Spell out numbers, currency, dates and abbreviations as words. "
    "Remove markdown, URLs and emoji. Keep the meaning and the order. "
    "Return only the rewritten text."
)

def normalize(text):
    r = client.chat.completions.create(
        model="uncensored",
        messages=[
            {"role": "system", "content": NORMALIZE},
            {"role": "user", "content": text},
        ],
        temperature=0.2,
    )
    return r.choices[0].message.content.strip()

print(normalize("Dr. Lee bought 3 copies on 3/4 for the 2nd edition."))

Gunakan suhu rendah untuk langkah ini. Anda ingin input yang sama memberikan output yang sama, dan kreativitas hanya akan mengganggu. Tinjau sampel hasil sebelum Anda menjalankan seluruh buku melaluinya, dan putuskan bagaimana Anda ingin menangani item ambigu. Tanggal "3/4" adalah contoh standar: bacalah sebagai 4 Maret atau 3 April? Masukkan pilihan Anda ke dalam prompt sistem, karena model tidak dapat menebak locale Anda.

Biaya langkah ini

Normalisasi itu murah. Paragraf 600 token masuk dan sekitar 650 token keluar biaya 600 x $0,15 / 1.000.000 = $0,00009 untuk input, ditambah 650 x $0,50 / 1.000.000 = $0,000325 untuk output, total $0,000415. Itu sekitar $0,42 untuk seribu paragraf. Halaman harga memiliki tabel lengkap dan opsi isi saldo.

Kembalikan segmen terstruktur dengan mode JSON

Beberapa mesin suara menerima pengaturan per-segmen: panjang jeda, kecepatan berbicara, suara berbeda untuk karakter yang berbeda. Anda dapat meminta model menghasilkan instruksi tersebut sebagai JSON dan membacanya di Python. Atur response_format ke {"type": "json_object"} dan juga gambarkan bentuknya dalam prompt, karena mode JSON menjamin JSON valid, bukan skema Anda.

import json
import os
from openai import OpenAI

client = OpenAI(base_url="https://api.xtts.cc/v1", api_key=os.environ["XTTS_API_KEY"])

prompt = (
    "Split this scene into segments. Return JSON like "
    '{"segments": [{"speaker": "narrator", "text": "...", "pause_ms": 300}]}. '
    "Speakers are narrator, mara, or tom."
)

r = client.chat.completions.create(
    model="uncensored",
    response_format={"type": "json_object"},
    messages=[
        {"role": "system", "content": prompt},
        {"role": "user", "content": "Mara opens the door. Tom says he is late again. She sighs and lets him in."},
    ],
    temperature=0.3,
)

data = json.loads(r.choices[0].message.content)
for seg in data["segments"]:
    print(seg["speaker"], seg["pause_ms"], seg["text"])

Validasikan apa yang kembali. Periksa apakah segments ada, apakah setiap item memiliki pembicara yang diketahui, dan apakah pause_ms adalah angka dalam rentang yang Anda terima. Jika validasi gagal, coba lagi sekali dengan suhu yang lebih rendah sebelum menyerah. Perlakukan output model sebagai input tidak tepercaya, sama seperti Anda akan menangani teks pengguna apa pun.

Jebakan yang memakan waktu

Beberapa kesalahan muncul lagi dan lagi ketika orang menghubungkan model bahasa di depan mesin suara.

  • Membiarkan model menambahkan komentar. Model suka mengatakan "Tentu, ini skrip Anda." Kalimat itu akan dibaca keras-keras. Akhiri prompt sistem Anda dengan "Kembalikan hanya teks skrip" dan hapus baris awal jika berakhir dengan titik dua.
  • Mempercayai panjang kalimat. Beberapa mesin kesulitan dengan kalimat yang sangat panjang. Tambahkan aturan seperti "tidak ada kalimat lebih dari 25 kata" ke prompt, dan juga terapkan di Python dengan membagi berdasarkan koma-koma ketika kalimat masih terlalu panjang.
  • Melupakan jendela konteks. Prompt dan penyelesaian berbagi 100.000 token. Untuk bab buku yang panjang, kirimkan dalam bagian-bagian dengan ringkasan singkat dari bagian sebelumnya daripada teks lengkap setiap kali. Itu juga menjaga tagihan input tetap rendah.
  • Memakai ulang satu klien global di seluruh thread tanpa batas. Klien aman untuk dibagikan, tetapi layanan tidak terbatas. Batas pool Anda pada konkurensi kunci Anda.
  • Mengkodekan kunci secara keras. Gunakan variabel lingkungan dan putar kunci jika itu pernah mendarat di repositori.

Saat sesuatu terlihat salah, catat header X-Request-Id dengan prompt yang menyebabkannya. Jika Anda perlu meminta bantuan, ID itu adalah cara tercepat untuk menunjuk ke satu panggilan tertentu, dan dokumentasi menjelaskan cara menemukannya.

Tangani batas laju dan percobaan ulang

Kunci penuh memungkinkan 8 permintaan konkuren dan 300 permintaan per menit. Kunci uji coba memungkinkan 2 konkuren dan 60 per menit. Melewati batas tersebut Anda mendapatkan HTTP 429, dengan kode kesalahan rate_limited untuk permintaan per menit atau concurrency untuk terlalu banyak sekaligus. Pada kelebihan beban sementara API juga menjawab 429 dan mengirimkan Retry-After: 5.

SDK mencoba lagi secara otomatis, tetapi pembungkus eksplisit memberi Anda kontrol dan membuat perilaku terlihat dalam log. Gabungkan dengan pool utas yang ukurannya disesuaikan dengan kunci Anda, dan Anda hampir tidak akan melihat 429 sama sekali:

import os
import time
from concurrent.futures import ThreadPoolExecutor
from openai import OpenAI, RateLimitError

client = OpenAI(
    base_url="https://api.xtts.cc/v1",
    api_key=os.environ["XTTS_API_KEY"],
    max_retries=0,
)

def ask(prompt, tries=5):
    for attempt in range(tries):
        try:
            r = client.chat.completions.create(
                model="uncensored",
                messages=[{"role": "user", "content": prompt}],
            )
            return r.choices[0].message.content
        except RateLimitError as err:
            wait = int(err.response.headers.get("Retry-After", 5))
            time.sleep(wait * (attempt + 1))
    raise RuntimeError("still rate limited after retries")

prompts = ["Narrate a sunrise in two sentences."] * 20
with ThreadPoolExecutor(max_workers=8) as pool:  # use 2 on a trial key
    results = list(pool.map(ask, prompts))
print(len(results))

Permintaan yang gagal atau ditolak tidak dibebankan biaya, sehingga mencoba lagi setelah kesalahan tidak membebani Anda untuk upaya yang gagal. Permintaan yang selesai dibebankan biaya secara normal, jadi hindari percobaan ulang buta pada panggilan lambat tetapi berhasil.

Serahkan teks ke mesin suara Anda

Langkah terakhir berada di luar API ini. Python memiliki beberapa cara untuk menghasilkan audio dari teks: model teks-ke-suara open-source yang dapat Anda jalankan sendiri, layanan teks-ke-suara desktop yang dibangun ke dalam sistem operasi, dan API teks-ke-suara yang dihosting dari perusahaan lain. Masing-masing memiliki lisensi, kualitas, dan harga sendiri, jadi periksa dokumentasi dan ketentuan terbaru mereka sebelum Anda memilih. Situs ini tidak menyediakan bagian itu.

Batas yang bersih menjaga kode Anda mudah diubah. Tulis satu fungsi dengan tanda tangan speak(text: str) -> None dan pertahankan setiap detail khusus mesin di dalamnya. Pipeline teks di atas memanggil fungsi itu untuk setiap kalimat atau segmen. Saat Anda menukar mesin, tidak ada yang lain berubah.

Jika Anda masih memutuskan antara uji coba dan pengaturan berbayar, angkanya sederhana: Anda membayar $0,15 per juta token input dan $0,50 per juta token output, dari saldo prabayar yang tidak pernah kedaluwarsa, tanpa langganan. Isi saldo berjalan dari $20 hingga $500 dan dapat dibayar dengan kripto (USDT pada TRC20, USDC pada Base).

Tanya jawab

Apakah API ini mengembalikan berkas audio?

Tidak. Ini hanya mengembalikan teks. Gunakan untuk menulis, membersihkan, dan menyusun skrip, lalu kirimkan teks ke mesin suara terpisah.

Paket Python apa yang harus Anda gunakan?

Paket openai resmi. Atur base_url ke https://api.xtts.cc/v1, masukkan kunci Anda, dan gunakan ID model uncensored.

Dapatkah saya melakukan streaming respons di Python?

Ya. Teruskan stream=True dan iterasi melalui potongan data. Penggunaan dilaporkan di akhir stream, dan permintaan mempertahankan slot konkurensinya hingga stream selesai.

Apa yang terjadi saat Anda mencapai batas laju?

Anda mendapatkan HTTP 429 dengan kode rate_limited atau concurrency. Tunggu nilai Retry-After jika ada dan coba lagi, dan pertahankan jumlah worker Anda di bawah atau sama dengan 8 pada kunci penuh atau 2 pada kunci uji coba.

Kunci Anda hanya selangkah lagi dari satu formulir

Buat akun, salin kunci, ubah URL dasar. Itu saja seluruh proses pengaturannya.