FR ▾
Commencer maintenant

API ChatGPT sans filtre : streaming, outils et JSON

Une API ChatGPT sans filtre n'est utile que si elle prend en charge ce dont ont besoin les applications réelles : texte en streaming, appels d'outils et sorties structurées. Cet endpoint propose ces trois fonctionnalités au format OpenAI. Voici du code fonctionnel pour chacune, ainsi qu'une liste claire des fonctionnalités non prises en charge.

Dernière mise à jour

Flux de particules lumineuses sur un convoyeur avec un bras robotique saisissant des blocs structurés, pour le streaming des outils et du JSON

Résumé

  1. Pris en charge : streaming via SSE, appel d'outils avec tool_calls en streaming, mode JSON et les paramètres d'échantillonnage habituels.
  2. Non pris en charge : génération d'images, d'audio ou de vidéo, embeddings, fine-tuning.
  3. Les flux occupent un slot de concurrence jusqu'à leur fin : 8 slots pour une clé complète, 2 pour une clé d'essai.
  4. Analysez les arguments d'outils et les réponses JSON de manière défensive ; les modèles peuvent renvoyer des sorties mal formées.

Ce qui est pris en charge et ce qui ne l'est pas

FonctionnalitéStatutNotes
Completions de chatOuiPOST /v1/chat/completions
RéponsesOuiPOST /v1/responses
Streaming (SSE)Ouistream: true, avec l'utilisation à la fin
Appel de fonctionsOuitools, tool_choice, streaming tool_calls, résultats en role: "tool"
Mode JSONOuiresponse_format: {"type":"json_object"}
Contrôles d'échantillonnageOuitemperature, top_p, stop, seed, pénalités
Images, audio, vidéoNonTexte uniquement
Embeddings, fine-tuningNonNon proposé

Le modèle est un modèle textuel à poids ouverts avec l'ID uncensored, géré par un fournisseur indépendant. Il répond sans refus de contenu pour un usage adulte légal et n'est pas l'API officielle d'OpenAI ou d'un autre fournisseur. Chaque exemple ci-dessous utilise l'URL de base https://api.chatgptunfiltered.com/v1.

Tout ce qui n'est pas dans cette liste doit être considéré comme indisponible jusqu'à ce que la documentation indique le contraire. Cela inclut les outils hébergés spécifiques au fournisseur, qui sont différents de l'appel de fonctions montré ci-dessous.

Diffusion en continu

Définissez stream: true et itérez sur les chunks. Affichez les morceaux de texte à mesure qu'ils arrivent et lisez l'utilisation dans le dernier chunk.

from openai import OpenAI

client = OpenAI(
    base_url="https://api.chatgptunfiltered.com/v1",
    api_key="YOUR_KEY",
)

stream = client.chat.completions.create(
    model="uncensored",
    messages=[{"role": "user", "content": "Write a four-line poem about a night train."}],
    stream=True,
)

for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
    if chunk.usage:
        print("\nusage:", chunk.usage)

Deux points à retenir. Premièrement, une requête en streaming occupe son slot de concurrence jusqu'à la fin du flux, donc un flux abandonné garde un slot occupé ; fermez-le lorsque l'utilisateur quitte. Deuxièmement, le dernier chunk peut contenir l'utilisation et aucun texte, donc protégez l'accès à choices comme indiqué ci-dessus.

Dans une application web, transférez les chunks vers le navigateur à mesure qu'ils arrivent, par exemple via votre propre route SSE, et laissez la page ajouter chaque morceau de texte au message. Gérez la comptabilité de l'utilisation sur le serveur une fois le flux fermé, où le dernier chunk vous indique le coût de la requête.

Appel de fonctions, de bout en bout

Déclarez une fonction, laissez le modèle la demander, exécutez-la vous-même et renvoyez le résultat avec role: "tool".

import json

tools = [{
    "type": "function",
    "function": {
        "name": "get_order_status",
        "description": "Look up the status of an order by its ID.",
        "parameters": {
            "type": "object",
            "properties": {"order_id": {"type": "string"}},
            "required": ["order_id"],
        },
    },
}]

def get_order_status(order_id):
    return {"order_id": order_id, "status": "shipped"}

messages = [{"role": "user", "content": "Where is order A-1042?"}]
r = client.chat.completions.create(
    model="uncensored", messages=messages, tools=tools, tool_choice="auto"
)
msg = r.choices[0].message

if msg.tool_calls:
    messages.append(msg.model_dump(exclude_none=True))
    for call in msg.tool_calls:
        args = json.loads(call.function.arguments)
        result = get_order_status(**args)
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": json.dumps(result),
        })
    final = client.chat.completions.create(model="uncensored", messages=messages, tools=tools)
    print(final.choices[0].message.content)
else:
    print(msg.content)

Le modèle décide s'il doit appeler la fonction ; tool_choice vous permet de l'orienter. La fonction elle-même s'exécute dans votre code, jamais sur le serveur, donc vous contrôlez ce qu'elle peut toucher. Validez les arguments avant d'agir, car un modèle peut envoyer un type incorrect ou un champ manquant.

Streaming des appels de fonctions

Lorsque vous combinez stream: true avec tools, l'appel arrive par morceaux. Le nom de la fonction arrive en premier, et la chaîne d'arguments arrive en fragments que vous devez assembler par index.

calls = {}

stream = client.chat.completions.create(
    model="uncensored",
    messages=[{"role": "user", "content": "Where is order A-1042?"}],
    tools=tools,
    stream=True,
)

for chunk in stream:
    if not chunk.choices:
        continue
    for tc in chunk.choices[0].delta.tool_calls or []:
        slot = calls.setdefault(tc.index, {"id": "", "name": "", "args": ""})
        if tc.id:
            slot["id"] = tc.id
        if tc.function and tc.function.name:
            slot["name"] = tc.function.name
        if tc.function and tc.function.arguments:
            slot["args"] += tc.function.arguments

for slot in calls.values():
    print(slot["name"], json.loads(slot["args"]))

N'analysez args qu'une fois le streaming terminé. Une chaîne partiellement reçue n'est pas un JSON valide. Ensuite, exécutez la fonction et renvoyez le résultat exactement comme dans l'exemple sans streaming.

Mode JSON

Pour des réponses structurées, activez le mode JSON et décrivez la forme souhaitée dans le prompt.

r = client.chat.completions.create(
    model="uncensored",
    temperature=0.2,
    response_format={"type": "json_object"},
    messages=[
        {"role": "system", "content": "Reply with JSON only: {\"mood\": string, \"intensity\": integer from 1 to 5}."},
        {"role": "user", "content": "Rate this line: The door slammed and nobody spoke for an hour."},
    ],
)

try:
    data = json.loads(r.choices[0].message.content)
except json.JSONDecodeError:
    data = None  # retry once, or fall back
print(data)

Le mode JSON rend un JSON valide beaucoup plus probable, mais il n'impose pas votre schéma. Vérifiez vous-même les clés et les types, et gardez une voie de réessai. Une température basse aide pour l'extraction. Les paramètres d'échantillonnage tels que temperature, top_p, stop, seed et les pénalités sont transmis.

Un motif utile consiste à combiner le mode JSON avec une petite fonction de validation qui renvoie un message d'erreur clair, puis à renvoyer ce message au modèle lors d'un seul réessai : "Votre dernière réponse manquait la clé intensity. Répondez à nouveau avec un JSON valide." Les modèles se corrigent généralement au deuxième essai, et la requête supplémentaire coûte une fraction de centime.

Gestion des échecs pendant un streaming

Les flux échouent de manières que les requêtes simples ne font pas : la connexion peut se couper après la moitié du texte. Prévoyez-le.

import time
from openai import RateLimitError, APIConnectionError

def stream_reply(messages):
    for attempt in range(3):
        parts = []
        try:
            stream = client.chat.completions.create(
                model="uncensored", messages=messages, stream=True
            )
            for chunk in stream:
                if chunk.choices and chunk.choices[0].delta.content:
                    parts.append(chunk.choices[0].delta.content)
            return "".join(parts)
        except RateLimitError:
            time.sleep(5)
        except APIConnectionError:
            time.sleep(1)
    raise RuntimeError("stream failed after retries")

Un 429 ici signifie une limite ou une surcharge temporaire, et la bonne démarche est d'attendre 5 secondes, ce qui correspond à Retry-After: 5 que l'API envoie. Si un flux se coupe à mi-parcours, vous avez une réponse partielle. Décidez si vous devez la rejeter et réessayer, ou la conserver et demander au modèle de continuer. Pour les interfaces de chat, rejeter et réessayer est généralement moins déroutant pour les utilisateurs.

Limites et coût à anticiper

  • Taux : 300 requêtes par minute pour une clé complète, 60 pour une clé d'essai.
  • Concurrence : 8 pour une clé complète, 2 pour une clé d'essai. N'oubliez pas que chaque flux ouvert compte.
  • Taille : corps de requête jusqu'à 8 Mo ; fenêtre de contexte de 100 000 tokens, prompt plus complétion.
  • Gestion du 429 : les codes rate_limited et concurrency signifient que vous avez dépassé une limite ; une surcharge temporaire renvoie 429 avec Retry-After: 5.

Exemple de coût : un échange avec appel de fonctions, deux requêtes, 1 200 tokens en entrée et 80 en sortie pour la première, 1 400 en entrée et 150 en sortie pour la seconde. L'entrée est de 2 600 x 0,15 $ / 1 M = 0,000 39 $. La sortie est de 230 x 0,50 $ / 1 M = 0,000 115 $. Total : 0,000 505 $. Les requêtes échouées ou refusées ne sont pas facturées. Les tarifs sont sur tarifs, et la liste complète des méthodes est dans la documentation. Commencez avec le crédit d'essai gratuit de 0,50 $ depuis la page des clés API. Pour en savoir plus sur le modèle, consultez comprendre les modèles ChatGPT sans censure. La limite stricte s'applique dans tous les modes : tout contenu sexuel impliquant des mineurs est interdit.

Une liste de contrôle avant le lancement

  • Les arguments des outils sont analysés avec json.loads dans un bloc try, et vérifiés par rapport aux clés et types attendus avant toute exécution.
  • Les fonctions que vous exposez ne peuvent faire que ce que vous permettriez à un utilisateur anonyme de faire. Le modèle choisit quand les appeler, pas vous.
  • Les réponses JSON sont validées, avec un seul réessai en cas d'échec.
  • Les flux sont fermés lorsque l'utilisateur quitte la page, afin qu'ils ne maintiennent pas de créneau de concurrence.
  • Votre limite de concurrence dans le code est à ou en dessous de la limite de la clé : 8 pour une clé complète.
  • Les IDs de requête de X-Request-Id sont enregistrés.
  • La clé vit sur le serveur et peut être régénérée ; l'ancienne cesse de fonctionner immédiatement.

Le service est uniquement textuel, donc une fonctionnalité nécessitant une image ou un embedding doit provenir d'un autre fournisseur.

Questions fréquentes

L'endpoint diffuse-t-il les réponses ?

Oui. Envoyez stream: true pour recevoir des Server-Sent Events, avec l'utilisation signalée à la fin du flux.

Prend-il en charge l'appel de fonctions ?

Oui. Utilisez tools et tool_choice, lisez tool_calls dans la réponse, et renvoyez les résultats en tant que messages avec le rôle tool. Les tool_calls en streaming sont également pris en charge.

Le mode JSON renverra-t-il toujours un JSON valide ?

Cela rend un JSON valide beaucoup plus probable, mais ce n'est pas une garantie, et il n'impose pas de schéma. Analysez le résultat dans un bloc try et réessayez en cas d'échec.

Puis-je utiliser le streaming et les outils dans la même requête ?

Oui. Les noms et les arguments des appels de fonctions arrivent par fragments ; assemblez-les par index et analysez les arguments une fois le streaming terminé.

Obtenez votre clé API

Inscrivez-vous, copiez le token, mettez à jour votre URL de base. C'est tout.

Commencer maintenant