TTS API Python: SDK के साथ बोलने के लिए पाठ तैयार करें
यह API ऑडियो लौटाता नहीं है। यह एक पाठ मॉडल है जिसे आप OpenAI SDK के साथ Python से कॉल करते हैं, और यह स्पीच इंजन के सामने फिट बैठता है: यह स्क्रिप्ट लिखता है, उसे साफ़ करता है और वाक्यों में विभाजित करता है जिसे आपकी वॉइस स्टेज पढ़ सकती है।
अपडेट किया गया

यह API क्या करता है और क्या नहीं करता
यदि आपने tts api python ट्यूटोरियल खोजा है, तो कोड से पहले एक बात स्पष्ट होनी चाहिए: यह सेवा स्पीच सिंथेसाइज़ नहीं करती। यह एक ओपन-वेट टेक्स्ट मॉडल के लिए POST /v1/chat/completions और GET /v1/models एक्सपोज़ करता है, जिसका मॉडल ID uncensored है। कोई ऑडियो एंडपॉइंट, कोई वॉइस लिस्ट और किसी भी रिस्पॉन्स में कोई ऑडियो फ़ाइल नहीं है। इसमें कोई इमेज, वीडियो या एम्बेडिंग आउटपुट भी नहीं है।
तो यह स्पीच प्रोजेक्ट में कहाँ फिट बैठता है? पाइपलाइन के टेक्स्ट हिस्से में। किसी वॉइस इंजन को स्क्रिप्ट पढ़ने से पहले, किसी को उसे लिखना होता है, संख्याओं और संक्षेपों को बोलने योग्य शब्दों में विस्तारित करना होता है, मार्कअप हटाना होता है, और इसे छोटे टुकड़ों में काटना होता है ताकि लंबी रुकावटों के बिना सिंथेसाइज़ किया जा सके। एक भाषा मॉडल यह काम अच्छी तरह करता है, और आप इसे Python से उसी SDK से कॉल कर सकते हैं जिसका आप पहले से अन्य OpenAI-संगत सेवाओं के लिए उपयोग कर रहे हैं।
मॉडल को कानूनी वयस्क उपयोग के लिए बिना सामग्री अस्वीकृति के उत्तर देने के लिए ट्यून किया गया है, जो कथा, चरित्र संवाद और विवरण के लिए महत्वपूर्ण है जहाँ एक कठोर मॉडल बीच में रुक सकता है। एक कठोर नियम लागू होता है: नाबालिगों से जुड़ी यौन सामग्री नहीं।
इस लेख का शेष भाग चार चरणों में एक छोटा पायथन मॉड्यूल तैयार करता है: एक पहला कॉल, एक स्ट्रीमिंग सेंटेंस स्प्लिटर, JSON आउटपुट के साथ एक टेक्स्ट नॉर्मलाइजर, और एक रेट लिमिट्स का सम्मान करने वाला रीट्राई व्रैपर।
SDK इंस्टॉल करें और पहला कॉल करें
आपको Python 3.8 या उससे नया और आधिकारिक openai पैकेज चाहिए। पहले API कुंजी पेज पर एक कुंजी बनाएं। नए खातों वहाँ एक क्लिक के साथ $0.50 का मुफ़्त ट्रायल क्रेडिट दावा कर सकते हैं, और इसके लिए कार्ड की आवश्यकता नहीं है।
pip install openai
कुंजी को अपने स्रोत से बाहर रखें। इसे एक एन्वायरनमेंट वेरिएबल के रूप में एक्सपोर्ट करें और कोड में इसे पढ़ें:
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)
यही पूरी इंटीग्रेशन है। SDK आपके लिए Authorization: Bearer <key> भेजता है। हर प्रतिक्रिया में एक X-Balance-USD हेडर भी होता है, इसलिए यदि आप इसे लॉग करना चाहते हैं तो आप कच्ची प्रतिक्रिया से अपनी शेष क्रेडिट पढ़ सकते हैं। दस्तावेज़ पृष्ठ अन्य हेडर और पैरामीटर सूचीबद्ध करता है।
temperature, top_p, stop, seed जैसे पैरामीटर और पेनल्टी पास किए जाते हैं। विवरण के लिए, 0.6 और 0.9 के बीच एक तापमान एक समझदारी भरी शुरुआती सीमा है, और एक निश्चित seed मदद करता है जब आप प्रॉम्प्ट ट्यूनिंग के दौरान एक टुकड़ा पुनः उत्पन्न करना चाहते हैं।
स्क्रिप्ट को स्ट्रीम करें और इसे वाक्यों में काटें
स्पीच इंजन आमतौर पर वाक्य-दर-वाक्य काम करते हैं। यदि आप 900 शब्दों के स्क्रिप्ट के समाप्त होने का प्रतीक्षा करते हैं, तो श्रोता भी प्रतीक्षा करता है। stream: true के साथ, API सर्वर-सेंट इवेंट्स के माध्यम से टोकन भेजता है, और आप प्रत्येक पूर्ण वाक्य को आवाज़ चरण को दे सकते हैं जबकि बाकी अभी भी लिखा जा रहा है।
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)
दो बातें आसानी से छूट सकती हैं। पहला, उपयोग के आंकड़े स्ट्रीम के अंत में आते हैं, एक ऐसे चंक में जो खाली choices लिस्ट हो सकता है, इसलिए लूप बिना चॉइस वाले चंक्स को छोड़ देता है। दूसरा, एक स्ट्रीम किए गए अनुरोध अपनी कॉन्करेंसी स्लॉट को स्ट्रीम समाप्त होने तक पकड़े रखता है। यदि आपकी वॉइस इंजन धीमी है और आप स्ट्रीम को लैज़ली पढ़ते हैं, तो स्लॉट व्यस्त रहता है। टेक्स्ट को जल्दी निकालें, वाक्यों को एक कतार में डालें, और ऑडियो साइड को कतार को अपने ही गति से खपाने दें।
ऊपर दिया गया रेगुलर एक्सप्रेशन स्पेस के साथ एक बिंदु या प्रश्न चिह्न के बाद विभाजित करता है। यह "Dr." या "No." जैसे संक्षेपों के बाद गलत तरीके से विभाजित करेगा, जो टेक्स्ट को पहले नॉर्मलाइज़ करने का एक और कारण है, जैसा कि अगला सेक्शन दिखाता है।
कान के लिए टेक्स्ट नॉर्मलाइज़ करें
लिखित पाठ और बोला गया पाठ अलग हैं। "डॉ. ली ने 3/4 को 2nd एडिशन के लिए 3 कॉपी खरीदीं" पढ़ने में आसान है और बोलने में अजीब। कई स्पीच इंजन इसका कुछ हिस्सा संभालते हैं, लेकिन परिणाम इंजन के अनुसार अलग होते हैं, इसलिए स्क्रिप्ट को वॉइस स्टेज तक पहुँचने से पहले साफ़ करना आश्चर्यों की एक श्रेणी को हटा देता है। मॉडल से इसे स्पष्ट निर्देश के साथ करने के लिए कहें:
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."))
इस स्टेप के लिए एक कम तापमान का उपयोग करें। आप चाहते हैं कि एक ही इनपुट एक ही आउटपुट दे, और रचनात्मकता केवल बाधा डालती है। एक पूरी बुक को इसके माध्यम से चलाने से पहले परिणामों के एक सैंपल की समीक्षा करें, और तय करें कि आप अस्पष्ट आइटम्स को कैसे हैंडल करना चाहते हैं। तारीख "3/4" एक मानक उदाहरण है: इसे 4 मार्च या 3 अप्रैल के रूप में पढ़ें? अपनी चॉइस को सिस्टम प्रॉम्प्ट में डालें, क्योंकि मॉडल आपके locale को नहीं अनुमान लगा सकता।
इस स्टेप की लागत
सामान्यीकरण सस्ता है। 600 टोकन का पैराग्राफ इन और लगभग 650 टोकन आउट की लागत 600 x $0.15 / 1,000,000 = $0.00009 इनपुट के लिए, प्लस 650 x $0.50 / 1,000,000 = $0.000325 आउटपुट के लिए, कुल $0.000415 है। यह हज़ार पैराग्राफ के लिए लगभग $0.42 है। मूल्य निर्धारण पृष्ठ पर पूर्ण तालिका और टॉप-अप विकल्प हैं।
JSON मोड के साथ संरचित सेगमेंट लौटाएं
कुछ आवाज़ इंजन प्रति-सेगमेंट सेटिंग्स स्वीकार करते हैं: एक विराम लंबाई, एक बोलने की दर, एक अलग पात्र के लिए एक अलग आवाज़। आप मॉडल से JSON में वे निर्देश उत्पन्न करवा सकते हैं और उन्हें Python में पढ़ सकते हैं। response_format को {"type": "json_object"} पर सेट करें और प्रॉम्प्ट में आकार का वर्णन भी करें, क्योंकि JSON मोड वैध JSON की गारंटी देता है, न कि आपके स्कीमा की।
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"])
जो वापस आता है उसकी जाँच करें। जाँच करें कि segments मौजूद है, कि हर आइटम का एक ज्ञात वक्ता है, और कि pause_ms एक संख्या है जो आपकी स्वीकार्य सीमा में है। यदि सत्यापन विफल होता है, तो तापमान कम करके एक बार पुनः प्रयास करें, फिर हार मानें। मॉडल के आउटपुट को अविश्वसनीय इनपुट के रूप में मानें, उसी तरह जैसे आप किसी भी यूजर टेक्स्ट को मानेंगे।
समय खर्च करने वाली चूकें
लोग जब एक स्पीच इंजन के सामने एक लैंग्वेज मॉडल वायर करते हैं तो कुछ गलतियाँ बार-बार सामने आती हैं।
- मॉडल को टिप्पणी जोड़ने देना। मॉडल कहना पसंद करते हैं "ठीक है, यहाँ आपका स्क्रिप्ट है।" यह वाक्य बोलकर सुनाया जाएगा। अपने सिस्टम प्रॉम्प्ट को "केवल स्क्रिप्ट टेक्स्ट लौटाएं" से समाप्त करें और एक शुरुआती पंक्ति को स्ट्रिप करें यदि वह कॉलन पर समाप्त होती है।
- वाक्य की लंबाई पर भरोसा करना। कुछ इंजन बहुत लंबे वाक्यों के साथ संघर्ष करते हैं। प्रॉम्प्ट में "कोई वाक्य 25 शब्द से लंबा नहीं" जैसे नियम जोड़ें, और पायथन में भी इसे लागू करें जब एक वाक्य अभी भी बहुत लंबा हो तो कॉमा पर विभाजित करके।
- कॉन्टेक्स्ट विंडो भूल जाना। प्रॉम्प्ट और कंप्लीशन 100,000 टोकन साझा करते हैं। एक लंबी बुक चैप्टर के लिए, इसे सेक्शन में भेजें पिछले सेक्शन के एक छोटे सारांश के साथ हर बार पूरे टेक्स्ट के बजाय। इससे इनपुट बिल भी नीचे रहता है।
- थ्रेड्स के बीच बिना किसी लिमिट के एक ग्लोबल क्लाइंट पुनः उपयोग करना। क्लाइंट साझा करने के लिए सुरक्षित है, लेकिन सर्विस अनंत नहीं है। अपने पूल को अपनी कुंजी की समवर्तीता तक सीमित करें।
- कुंजी को हार्ड-कोड करना। एक एनवायरनमेंट वेरिएबल का उपयोग करें और यदि कुंजी कभी एक रिपॉजिटरी में आ जाती है तो उसे रोटेट करें।
जब कुछ गलत लगता है, तो X-Request-Id हेडर को उस प्रॉम्प्ट के साथ लॉग करें जिसने इसे ट्रिगर किया। यदि आपको मदद मांगनी है, तो वह ID एक विशिष्ट कॉल की ओर इशारा करने का सबसे तेज़ तरीका है, और docs बताती हैं कि इसे कहाँ खोजना है।
रेट लिमिट और पुन: प्रयास हैंडल करें
एक फुल कुंजी 8 समवर्ती अनुरोधों और प्रति मिनट 300 अनुरोधों की अनुमति देती है। एक ट्रायल कुंजी 2 समवर्ती और प्रति मिनट 60 की अनुमति देती है। किसी भी सीमा से परे जाने पर आपको HTTP 429 मिलता है, प्रति मिनट अनुरोधों के लिए error code rate_limited और एक साथ बहुत सारे अनुरोधों के लिए concurrency के साथ। अस्थायी ओवरलोड पर API भी 429 उत्तर देता है और Retry-After: 5 भेजता है।
SDK स्वयं पुनः प्रयास करता है, लेकिन एक स्पष्ट रैपर आपको नियंत्रण देता है और व्यवहार को लॉग में दिखाता है। इसे अपनी कुंजी के लिए आकार दिए गए थ्रेड पूल के साथ जोड़ें, और आपको 429 बहुत कम दिखेगा:
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))
असफल या अस्वीकृत अनुरोधों के लिए चार्ज नहीं किया जाता है, इसलिए त्रुटि के बाद पुन: प्रयास करने से असफल प्रयास के लिए आपको कोई लागत नहीं आती। एक पूर्ण अनुरोध सामान्य रूप से बिल किया जाता है, इसलिए धीमी लेकिन सफल कॉलों पर अंधाधुंध पुन: प्रयास से बचें।
टेक्स्ट को अपने स्पीच इंजन को दें
अंतिम चरण इस API के बाहर है। पायथन में टेक्स्ट से ऑडियो बनाने के कई तरीके हैं: ओपन-सोर्स स्पीच मॉडल जिन्हें आप स्वयं चला सकते हैं, ऑपरेटिंग सिस्टम में बिल्ट-इन डेस्कटॉप स्पीच सर्विसें, और अन्य कंपनियों की होस्टेड स्पीच APIs। प्रत्येक की अपनी लाइसेंस, गुणवत्ता और कीमत होती है, इसलिए चुनने से पहले उनकी वर्तमान दस्तावेज़ीकरण और शर्तों की जाँच करें। यह साइट उस भाग को प्रदान नहीं करती है।
एक क्लीन बाउंडरी आपके कोड को बदलने में आसान बनाती है। speak(text: str) -> None साइनचर के साथ एक फंक्शन लिखें और हर इंजन-विशिष्ट विवरण को इसके अंदर रखें। ऊपर दिया गया टेक्स्ट पाइपलाइन उस फंक्शन को प्रत्येक वाक्य या सेगमेंट के लिए कॉल करता है। जब आप इंजन बदलते हैं, तो कुछ और नहीं बदलता।
यदि आप अभी भी ट्रायल और भुगतान वाले सेटअप के बीच निर्णय ले रहे हैं, तो संख्याएँ सरल हैं: आप प्रीपेड क्रेडिट से प्रति मिलियन इनपुट टोकन के लिए $0.15 और प्रति मिलियन आउटपुट टोकन के लिए $0.50 भुगतान करते हैं, जो कभी समाप्त नहीं होता, कोई सब्सक्रिप्शन नहीं है। टॉप-अप $20 से $500 तक होते हैं और क्रिप्टो (TRC20 पर USDT, Base पर USDC) से भुगतान किए जा सकते हैं।
अधिक गाइड
प्रश्न और उत्तर
क्या यह API ऑडियो फ़ाइलें लौटाता है?
नहीं। यह केवल टेक्स्ट लौटाता है। इसे स्क्रिप्ट लिखने, साफ़ करने और संरचित करने के लिए उपयोग करें, फिर टेक्स्ट को एक अलग स्पीच इंजन को दें।
मुझे किस Python पैकेज का उपयोग करना चाहिए?
आधिकारिक openai पैकेज। base_url को https://api.xtts.cc/v1, पर सेट करें, अपनी कुंजी पास करें, और मॉडल ID uncensored का उपयोग करें।
क्या मैं पायथन में स्ट्रीमिंग प्रतिक्रियाएँ प्राप्त कर सकता हूँ?
हाँ। stream=True पास करें और चंक पर इटरेट करें। उपयोग स्ट्रीम के अंत में रिपोर्ट किया जाता है, और अनुरोध स्ट्रीम समाप्त होने तक अपनी समानांतर अनुरोध स्लॉट रखता है।
जब मैं रेट लिमिट पर पहुँचता हूँ तो क्या होता है?
आपको rate_limited या concurrency कोड के साथ HTTP 429 मिलता है। यदि Retry-After मान मौजूद हो तो उसका प्रतीक्षा करें और पुन: प्रयास करें, और अपनी वर्कर संख्या को पूर्ण कुंजी पर 8 या ट्रायल कुंजी पर 2 से कम या बराबर रखें।
आपकी कुंजी बस एक फ़ॉर्म दूर है
एक खाता बनाएँ, कुंजी कॉपी करें, बेस URL बदलें। सेटअप यही है।