AR ▾

واجهة برمجة نصية بدون رقابة لخطوط إنتاج الصوت

uncensoredhttps://api.xtts.cc/v1

عرض تجريبي لواجهة برمجة تطبيقات تحويل النص إلى كلام: طلبات واستجابات حية

يُظهر هذا العرض طلبات حقيقية يمكنك تشغيلها في دقيقة واحدة، وتنسيق الاستجابة الذي يعود. الواجهة هي نموذج نصي، لذا يكون المخرج نصًا للسيناريو وJSON واستدعاءات الدوال التي يمكن لمحرك نطق منفصل قراءتها بصوت عالٍ.

تم التحديث

رسم توضيحي إسقاطي لواجهة طرفية تُخرج نصًا وبيانات وتدفقات استدعاء الدوال

قبل تشغيل أي شيء

تحذير لكل من يتوقع الضغط على زر التشغيل لسماع صوت: هذه التجربة العملية لواجهة TTS لا تُخرج صوتًا. تُرجع الخدمة نصًا من نموذج واحد، uncensored، عبر POST /v1/chat/completions. هناك نقطة نهاية ثانية، GET /v1/models، لسرد ما هو متاح. لا تُرجع أي شيء صوتًا أو صورًا أو تمثيلات متجهة (embeddings).

لماذا عرض توضيحي إذن؟ لأن العديد من مشاريع الكلام تحتاج بالضبط إلى هذا الجزء: كاتب نصوص، منظف للنصوص، ومقسم يقرر من يقول ماذا. تُظهر الأمثلة أدناه هذه المهام، وتنسيق الإخراج هو ما سيقوم كود الكلام بتحليله.

تحتاج إلى مفتاح. سجّل في صفحة مفتاح API باستخدام Google أو عبر البريد الإلكتروني وكلمة المرور، ثم استرد رصيد التجربة المجاني بقيمة $0.50 بنقرة واحدة. صالح لمدة 7 أيام ولا يحتاج إلى بطاقة. قم بتصدير المفتاح حتى تتمكن الأوامر أدناه من قراءته:

export XTTS_API_KEY="paste-your-key-here"
curl -s https://api.xtts.cc/v1/models -H "Authorization: Bearer $XTTS_API_KEY" | grep uncensored

إذا أرجعت هذه المكالمة قائمة JSON تتضمن uncensored، فإن المفتاح يعمل ويمكنك المتابعة.

العرض التجريبي 1: طلب عادي

أصغر مكالمة مفيدة تطلب فقرة قصيرة بأسلوب الكلام:

curl -s https://api.xtts.cc/v1/chat/completions \
  -H "Authorization: Bearer $XTTS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "uncensored",
    "messages": [
      {"role": "system", "content": "You write short lines for a voice actor. No stage directions."},
      {"role": "user", "content": "Welcome listeners to a show about lighthouses. Two sentences."}
    ],
    "temperature": 0.7,
    "max_tokens": 120
  }'

تبدو الإجابة كما هو موضح في العينة أدناه. سيختلف النص داخل content في كل تنفيذ، لذا تعامل معه كتوضيح للبنية وليس كإخراج مسجل.

{
  "id": "chatcmpl-example",
  "object": "chat.completion",
  "model": "uncensored",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Welcome to the show. Tonight we climb the stairs of the world's loneliest lighthouses."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {"prompt_tokens": 38, "completion_tokens": 21, "total_tokens": 59}
}

يقرأ كود تحويل النص إلى كلام choices[0].message.content. يُخبرك finish_reason عن سبب انتهاء التوليد: stop لنهاية طبيعية، length إذا كان max_tokens قد قطعها قبل أوانها. إذا تركت max_tokens خارج الطلب، فقد تستخدم الإكمال أي جزء متبقي من نافذة الـ 100,000 رمز، والتي تشمل الموجه والإكمال معًا.

ما تخبرك به الرؤوس

أضف -i إلى أمر curl وسترى X-Request-Id، X-Balance-USD، X-RateLimit-Limit-Requests و X-RateLimit-Limit-Concurrency. رأس الرصيد مفيد في العرض التجريبي لأنه يمكنك مراقبة انخفاضه بمقدار ضئيل بعد كل استدعاء. بالنسبة لهذا الطلب، يتحول الاستخدام المذكور أعلاه إلى 38 × $0.15 / 1,000,000 زائد 21 × $0.50 / 1,000,000، وهو ما يساوي $0.0000057 زائد $0.0000105، أي حوالي $0.0000162.

العرض التجريبي 2: بث النص

بالنسبة إلى الصوت، نادرًا ما ترغب في انتظار النص الكامل. أضف "stream": true وترسل الواجهة أحداث المرسلة من الخادم (SSE)، قطعة نص صغيرة في كل مرة. يطبع مثال Node أدناه النص عند وصوله، وهو المكان الذي ستقوم فيه بإدخال قائمة جمل.

import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://api.xtts.cc/v1",
  apiKey: process.env.XTTS_API_KEY,
});

const stream = await client.chat.completions.create({
  model: "uncensored",
  stream: true,
  messages: [
    { role: "user", content: "Read a four-line weather report for a coastal town." },
  ],
});

for await (const chunk of stream) {
  const piece = chunk.choices[0]?.delta?.content ?? "";
  process.stdout.write(piece);
}
process.stdout.write("\n");

قم بتشغيله كوحدة ES (احفظه باسم demo.mjs وشغّل node demo.mjs بعد npm install openai). على السلك، كل حدث هو سطر يبدأ بـ data: متبوعًا بـ JSON، وينتهي التدفق بعلامة نهائية. يُبلغ عن استخدام الرموز في نهاية التدفق. نقطة واحدة مهمة للتخطيط: يحافظ الطلب المتدفق على شريحة التزامن الخاصة به حتى ينتهي التدفق، لذا يمكن أن يحجز المستهلك البطيء طلبك التالي.

التجربة 3: استدعاء الدوال كموجّه صوتي

يسمح استدعاء الدوال للنموذج بإرجاع تعليمات منظمة بدلاً من نثر. في مشروع صوتي، هذه طريقة نظيفة لاختيار أسلوب تقديم لكل سطر. تُعرّف دالة باسم say، ويملأ النموذج حججها. يقرر كودك بعد ذلك ماذا يفعل بها. لا يشغل النموذج أي شيء بنفسه.

import json
import os
from openai import OpenAI

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

tools = [{
    "type": "function",
    "function": {
        "name": "say",
        "description": "Queue one line for the voice engine.",
        "parameters": {
            "type": "object",
            "properties": {
                "text": {"type": "string"},
                "style": {"type": "string", "enum": ["calm", "urgent", "whisper"]},
            },
            "required": ["text", "style"],
        },
    },
}]

messages = [
    {"role": "system", "content": "Always answer by calling say, once per line."},
    {"role": "user", "content": "The alarm sounds. The guard tells everyone to leave."},
]

r = client.chat.completions.create(
    model="uncensored", messages=messages, tools=tools, tool_choice="auto"
)

msg = r.choices[0].message
for call in msg.tool_calls or []:
    args = json.loads(call.function.arguments)
    print(args["style"], "->", args["text"])

تصل الحجج كسلسلة JSON، لذا قم بتحليلها والتحقق منها قبل الاستخدام. يمكن للنماذج إنتاج نمط خارج نطاق القائمة الثابتة (enum) أو حذف حقل، ويجب ألا تصل قيمة غير متحققة منها إلى محركك. إذا كنت تريد استمرار المحادثة، ألصق رسالة المساعد وردًا بـ role: "tool" ومعرف استدعاء الدالة المطابق tool_call_id، ثم اتصل بالواجهة مرة أخرى.

عند البث، تصل استدعاءات الدوال على أجزاء. يأتي اسم الدالة أولاً وتبنى سلسلة الحجة عبر الأجزاء، لذا اجمع الأجزاء حسب الفهرس وقم بالتحليل بمجرد انتهاء البث.

العرض التجريبي 4: وضع JSON لقائمة الطاقم

إذا كنت تفضل تخطي تعريفات الأدوات، يوفر وضع JSON JSON صالحاً في محتوى الرسالة. إنه مناسب جيد لتحويل مشهد مبدئي إلى أسطر لكل متحدث.

curl -s https://api.xtts.cc/v1/chat/completions \
  -H "Authorization: Bearer $XTTS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "uncensored",
    "response_format": {"type": "json_object"},
    "temperature": 0.2,
    "messages": [
      {"role": "system", "content": "Return JSON: {\"lines\": [{\"speaker\": string, \"text\": string}]}"},
      {"role": "user", "content": "Ana asks Ben to hold the ladder. Ben agrees reluctantly."}
    ]
  }'

يضمن وضع JSON أن الإجابة قابلة للتحليل، وليس أنها تطابق الشكل المطلوب. صف الشكل في الموجه، كما هو موضح أعلاه، وتحقق من المفاتيح في الرمز. درجة الحرارة المنخفضة تحافظ على استقرار البنية.

التجربة 5: مستند طويل في طلب واحد

نافذة السياق هي 100,000 رمز، مشتركة بين الموجه والإكمال. هذا يكفي لإرسال نص طويل أو قصة قصيرة كاملة وطلب نسخة مكتوبة للأذن. يمكن أن يصل حجم جسم الطلب إلى 8 ميجابايت، لذا نادرًا ما يكون الحجم هو الحد؛ عدد الرموز هو.

إليك تكلفة مكالمة ثقيلة. لنفترض أنك أرسلت 80,000 رمز وحصلت على 2,000. المدخلات: 80,000 × $0.15 / 1,000,000 = $0.012. المخرجات: 2,000 × $0.50 / 1,000,000 = $0.001. المجموع هو $0.013، لذا سيغطي عرض التجربة بقيمة $0.50 حوالي 38 مكالمات مثل هذه. المدخلات الطويلة رخيصة مقارنة بالمخرجات الطويلة، ولهذا السبب تكون الملخصات وإعادة صياغة النصوص الكبيرة معقولة التكلفة بينما توليد رواية كاملة دفعة واحدة ليس ممكنًا.

نصيحة عملية للمدخلات الطويلة: ضع تعليماتك في البداية وكرر القاعدة الأساسية في النهاية. تميل النماذج إلى اتباع أحدث تعليمات بشكل أفضل، وتكاد لا تكلف شيئاً إضافة تذكير بسطر واحد بعد 80,000 رمز من النص المصدر.

أخطاء القراءة في العرض التوضيحي

أدخل خطأً عن قصد مرة واحدة لتتعرف عليه لاحقاً. أرسل الطلب بمفتاح خاطئ وستحصل على خطأ مصادقة بدلاً من الإكمال. أرسل عشرة طلبات في نفس اللحظة باستخدام مفتاح تجريبي وستعود بعضها بـ HTTP 429.

لخطأ 429 ثلاثة أسباب، ويخبرك رمز الخطأ بتمييزها:

  • يعني rate_limited وجود طلبات كثيرة جداً في الدقيقة الواحدة: 60 لمفتاح تجريبي، و300 لمفتاح كامل.
  • يعني concurrency وجود طلبات كثيرة جداً قيد التنفيذ في آن واحد: 2 لمفتاح تجريبي، و8 لمفتاح كامل.
  • يعني وجود رأس 429 مع Retry-After: 5 أن الخدمة كانت مثقلة مؤقتاً. انتظر خمس ثوانٍ وأرسل الطلب مرة أخرى.

الطلبات الفاشلة أو المرفوضة لا تُحسب تكلفتها، لذا لا تكلف الأخطاء شيئاً. لرؤية الحدود التي يسمح بها مفتاحك، اقرأ X-RateLimit-Limit-Requests وX-RateLimit-Limit-Concurrency في أي استجابة عادية. حلقة إعادة محاولة صغيرة تتوقف (تنام) لمدة قيمة Retry-After، أو لخمس ثوانٍ إذا كانت مفقودة، هي كل ما يحتاجه نص تجريبي.

ما الذي يجب تجربته بعد ذلك

عندما تعمل هذه المكالمات الأربع، تكون لديك قطع خط أنابيب نصي. غلف الطلب العادي في دالة، وشغّل حلقة البث المتدفق إلى طابور، واربط الطابور بمحرك الكلام الذي تختاره. تختلف محركات الصوت في السعر والترخيص والجودة، لذا اقرأ شروطها الحالية. لا توفر هذه الخدمة محركاً ولا تذكر شيئاً بشأنها.

تظل التكاليف صغيرة أثناء تجربتك. بسعر $0.15 لكل مليون رمز مدخل و$0.50 لكل مليون رمز مخرج، تكلف تجربة طلب نموذجية جزءًا من سنت، ويغطي رصيد التجربة المجاني بقيمة $0.50 مئات منها. انظر إلى صفحة التسعير لشحن الرصيد من $20 إلى $500 مع مكافآت من 5% إلى 15%، بالدفع بالعملات المشفرة فقط. تغطي الوثائق قائمة المعاملات الكاملة، وحدّ المعدل (8 متزامنة و300 في الدقيقة بمفتاح كامل) وأكواد الأخطاء. لا تُستخدم الموجهات للتدريب.

ترتيب معقول لجلسة الظهيرة الأولى: أرسل الطلب العادي حتى يصبح النبرة صحيحة، ثم فعّل البث المتدفق وطبع الجمل عند اكتمالها، ثم أضف إما استدعاء الدوال أو وضع JSON، حسب الطريقة التي يريد بها كود التحويل الصوتي استقبال مدخلاته. احتفظ بكل موجّه في ملف تحت نظام التحكم بالإصدارات. عندما يبدو سطر ما خاطئاً في الصوت النهائي، يمكنك تتبعه إلى الموجّه الدقيق الذي أنتجه وتغيير شيء واحد في كل مرة.

أسئلة وأجوبة

هل يمكنني سماع عينة صوتية في هذا العرض التجريبي؟

لا. تُرجع الـ API نصاً وJSON واستدعاءات الدوال فقط. أنت تزود محرك تحويل صوتي منفصل لتحويل النص إلى صوت.

ما هو معرف النموذج الذي يجب إرساله؟

أرسل uncensored كقيمة للنموذج، مع عنوان URL الأساسي https://api.xtts.cc/v1 ومفتاحك في رأس Authorization: Bearer.

هل تعمل العروض التجريبية على مفتاح تجريبي؟

نعم. يسمح المفتاح التجريبي بـ 2 طلب متزامن و60 في الدقيقة، وهو ما يكفي لهذه الأمثلة. شغّلها واحدة تلو الأخرى.

لماذا أحصل على استجابة 429؟

لقد تجاوزت حدّ الطلبات في الدقيقة (rate_limited)، أو كان لديك طلبات متزامنة (concurrency) كثيرة جداً، أو كانت الخدمة مثقلة مؤقتاً. انتظر قيمة Retry-After إن وُجدت، ثم أعد المحاولة.

مفتاحك على بُعد استمارة واحدة

أنشئ حساباً، وانسخ المفتاح، ثم غيّر URL الأساسي. هذه هي الإعدادات الكاملة.