FR ▾

API de texte sans censure pour pipelines vocaux

uncensoredhttps://api.xtts.cc/v1

Démo API TTS : Exemples de requêtes et réponses en direct

Cette démo montre des requêtes réelles que vous pouvez exécuter en une minute et le format de réponse qui est renvoyé. L'API étant un modèle textuel, la sortie est du texte de script, du JSON et des appels de fonctions qu'un moteur de synthèse vocal séparé peut ensuite lire à voix haute.

Mis à jour le

Illustration isométrique d'un terminal émettant du texte, des données et des flux d'appels de fonctions

Avant de lancer quoi que ce soit

Un avertissement pour quiconque s'attend à appuyer sur lecture et entendre une voix : cette démo de l'API tts ne produit aucun son. Le service renvoie du texte depuis un modèle, uncensored, via POST /v1/chat/completions. Il existe un second endpoint, GET /v1/models, pour lister ce qui est disponible. Rien ne renvoie d'audio, d'images ou d'embeddings.

Pourquoi une démo alors ? Parce que beaucoup de projets vocaux ont exactement besoin de cette partie : un rédacteur de script, un nettoyeur de texte, un séparateur qui décide qui dit quoi. Les exemples ci-dessous montrent ces tâches, et le format de sortie est ce que votre code vocal analysera.

Vous avez besoin d'une clé. Inscrivez-vous sur la page clé API avec Google ou avec e-mail et mot de passe, puis réclamez le crédit d'essai de 0,50 $ en un clic. Il est valable 7 jours et ne nécessite pas de carte. Exportez la clé afin que les commandes ci-dessous puissent la lire :

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

Si cet appel renvoie une liste JSON incluant uncensored, la clé fonctionne et vous pouvez continuer.

Démo 1 : une requête simple

L'appel le plus utile minimal demande un court paragraphe de style oral :

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
  }'

La réponse ressemble à l'exemple ci-dessous. Le texte à l'intérieur de content différera à chaque exécution, traitez-le donc comme une illustration de la structure et non comme une sortie enregistrée.

{
  "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}
}

Votre code de lecture de parole lit choices[0].message.content. Le finish_reason vous indique pourquoi la génération s'est arrêtée : stop pour une fin naturelle, length si max_tokens l'a coupée prématurément. Si vous omettez max_tokens, le texte généré peut utiliser tout ce qui reste de la fenêtre de contexte de 100 000 tokens, qui couvre le prompt et le texte généré ensemble.

Ce que les en-têtes vous indiquent

Ajoutez -i à la commande curl et vous verrez X-Request-Id, X-Balance-USD, X-RateLimit-Limit-Requests et X-RateLimit-Limit-Concurrency. L'en-tête de solde est utile dans une démo car vous pouvez voir baisser un tout petit montant après chaque appel. Pour cette requête, l'utilisation ci-dessus revient à 38 x 0,15 $ / 1 000 000 plus 21 x 0,50 $ / 1 000 000, soit 0,0000057 $ plus 0,0000105 $, environ 0,0000162 $.

Démo 2 : diffusion en streaming du script

Pour l'audio, vous ne voulez rarement attendre le texte complet. Ajoutez "stream": true et l'API envoie des événements envoyés par le serveur, un petit morceau de texte à la fois. L'exemple Node ci-dessous affiche le texte à mesure qu'il arrive, ce qui est là où vous alimenteriez une file d'attente de phrases.

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");

Exécutez-le en tant que module ES (enregistrez sous demo.mjs et exécutez node demo.mjs après npm install openai). Sur le réseau, chaque événement est une ligne commençant par data: suivie de JSON, et le flux se ferme avec un marqueur final. L'utilisation de tokens est rapportée à la fin du flux. Un point important pour la planification : une requête en streaming conserve sa place de concurrence jusqu'à la fin du flux, donc un consommateur lent peut bloquer votre prochaine requête.

Démo 3 : appel d'outils comme routeur de parole

L'appel d'outils permet au modèle de renvoyer une instruction structurée au lieu de texte. Dans un projet vocal, c'est un moyen propre de choisir un style de livraison par ligne. Vous définissez une fonction appelée say, et le modèle remplit ses arguments. Votre code décide ensuite quoi en faire. Le modèle n'exécute rien lui-même.

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"])

Les arguments arrivent sous forme de chaîne JSON, donc analysez-les et validez-les avant utilisation. Les modèles peuvent produire un style hors de votre énumération ou omettre un champ, et une valeur non vérifiée ne doit jamais atteindre votre moteur. Si vous voulez que la conversation continue, ajoutez le message de l'assistant et une réponse avec role: "tool" et l'identifiant tool_call_id correspondant, puis appelez à nouveau l'API.

En streaming, les appels de fonctions arrivent par morceaux. Le nom de la fonction arrive en premier et la chaîne d'arguments se construit sur plusieurs fragments, donc collectez les fragments par index et analysez-les une fois le flux terminé.

Démo 4 : mode JSON pour une distribution

Si vous préférez sauter les définitions de fonctions, le mode JSON donne du JSON valide dans le contenu du message. C'est un bon choix pour transformer une scène approximative en lignes par locuteur.

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."}
    ]
  }'

Le mode JSON garantit que la sortie s'analyse, pas qu'elle correspond à votre forme. Décrivez la forme dans le prompt, comme ci-dessus, et vérifiez les clés dans le code. Une température basse maintient la structure stable.

Démo 5 : un long document en une seule requête

La fenêtre de contexte est de 100 000 tokens, partagée par le prompt et la complétion. Cela suffit pour envoyer une longue transcription ou une courte histoire entière et demander une version écrite pour l'oreille. Le corps de la requête peut faire jusqu'à 8 Mo, donc la taille est rarement la limite ; c'est le nombre de tokens qui l'est.

Voici le coût d'un appel lourd. Disons que vous envoyez 80 000 tokens et en recevez 2 000. Entrée : 80 000 x 0,15 $ / 1 000 000 = 0,012 $. Sortie : 2 000 x 0,50 $ / 1 000 000 = 0,001 $. Le total est de 0,013 $, donc le crédit d'essai de 0,50 $ couvrirait environ 38 appels comme celui-ci. L'entrée longue est bon marché comparée à la sortie longue, c'est pourquoi les résumés et réécritures de gros textes sont abordables tandis que générer un roman entier d'un coup ne l'est pas.

Un conseil pratique pour les entrées longues : mettez vos instructions en premier et répétez la règle clé à la fin. Les modèles ont tendance à suivre l'instruction la plus récente au mieux, et un rappel d'une ligne après 80 000 tokens de texte source coûte presque rien.

Lire les erreurs dans la démo

Cassez quelque chose intentionnellement une fois pour le reconnaître plus tard. Envoyez la requête avec une clé incorrecte et vous obtiendrez une erreur d'authentification au lieu d'une complétion. Envoyez dix requêtes en même instant depuis une clé d'essai et certaines reviendront en HTTP 429.

Le 429 a trois causes, et le code d'erreur les distingue :

  • rate_limited signifie trop de requêtes en une minute : 60 sur une clé d'essai, 300 sur une clé complète.
  • concurrency signifie trop de requêtes en vol à la fois : 2 sur une clé d'essai, 8 sur une clé complète.
  • Un 429 avec un en-tête Retry-After: 5 signifie que le service était brièvement surchargé. Attendez cinq secondes et envoyez-le à nouveau.

Les requêtes échouées ou refusées ne sont pas facturées, donc les erreurs ne coûtent rien. Pour voir les limites de votre clé, lisez X-RateLimit-Limit-Requests et X-RateLimit-Limit-Concurrency sur n'importe quelle réponse normale. Une petite boucle de nouvelle tentative qui dort pendant la valeur Retry-After, ou cinq secondes quand elle est manquante, suffit pour un script de démonstration.

Que faire ensuite

Une fois que ces quatre appels fonctionnent, vous avez les éléments d'un pipeline de script. Enveloppez la requête brute dans une fonction, exécutez la boucle de streaming dans une file d'attente, et connectez la file d'attente au moteur de parole de votre choix. Les moteurs diffèrent en prix, licence et qualité, donc lisez leurs conditions actuelles. Ce service n'en fournit pas et ne déclare rien à leur sujet.

Les coûts restent faibles pendant que vous expérimentez. À 0,15 $ par million de tokens d'entrée et 0,50 $ par million de tokens de sortie, un appel de démo typique coûte une fraction de centime, et le crédit d'essai de 0,50 $ en couvre des centaines. Consultez la page tarifs pour les recharges de 20 $ à 500 $ avec des bonus de 5 % à 15 %, payables par crypto. Les docs couvrent la liste complète des paramètres, les limites de débit (8 simultanés et 300 par minute sur une clé complète) et les codes d'erreur. Les prompts ne sont pas utilisés pour l'entraînement.

Un ordre raisonnable pour un premier après-midi : exécutez la requête simple jusqu'à ce que le ton soit correct, passez au streaming et imprimez les phrases à mesure qu'elles sont terminées, puis ajoutez soit des appels de fonctions soit le mode JSON, selon la façon dont votre code vocal veut son entrée. Gardez chaque prompt dans un fichier sous contrôle de version. Quand une ligne sonne faux dans l'audio final, vous pouvez alors la remonter jusqu'à l'instruction exacte qui l'a produite et changer une chose à la fois.

Questions et réponses

Puis-je écouter un échantillon de voix dans cette démo ?

Non. L'API renvoie uniquement du texte, du JSON et des appels de fonctions. Vous fournissez un moteur de synthèse vocal séparé pour convertir le texte en audio.

Quel ID de modèle envoyer ?

Envoyez uncensored comme valeur du modèle, avec l'URL de base https://api.xtts.cc/v1 et votre clé dans l'en-tête Authorization: Bearer.

Les démos fonctionnent-elles avec une clé d'essai ?

Oui. Une clé d'essai autorise 2 requêtes simultanées et 60 par minute, ce qui est largement suffisant pour ces exemples. Exécutez-les une par une.

Pourquoi obtiens-je une réponse 429 ?

Vous avez dépassé les requêtes par minute (limite de débit dépassée), eu trop de requêtes ouvertes à la fois (concurrence), ou le service était brièvement surchargé. Attendez Retry-After si indiqué, puis réessayez.

Votre clé est à un formulaire de vous

Créez un compte, copiez la clé, modifiez l'URL de base. C'est toute la configuration.