Ce qui est pris en charge et ce qui ne l'est pas
| Fonctionnalité | Statut | Notes |
|---|---|---|
| Completions de chat | Oui | POST /v1/chat/completions |
| Réponses | Oui | POST /v1/responses |
| Streaming (SSE) | Oui | stream: true, avec l'utilisation à la fin |
| Appel de fonctions | Oui | tools, tool_choice, streaming tool_calls, résultats en role: "tool" |
| Mode JSON | Oui | response_format: {"type":"json_object"} |
| Contrôles d'échantillonnage | Oui | temperature, top_p, stop, seed, pénalités |
| Images, audio, vidéo | Non | Texte uniquement |
| Embeddings, fine-tuning | Non | Non 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_limitedetconcurrencysignifient que vous avez dépassé une limite ; une surcharge temporaire renvoie 429 avecRetry-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.loadsdans 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-Idsont 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.
