Was unterstützt wird und was nicht
| Merkmal | Status | Hinweise |
|---|---|---|
| Chat-Vervollständigungen | Ja | POST /v1/chat/completions |
| Antworten | Ja | POST /v1/responses |
| Streaming (SSE) | Ja | stream: true, mit Nutzung am Ende |
| Function Calling | Ja | tools, tool_choice, gestreamte tool_calls, Ergebnisse als role: "tool" |
| JSON-Modus | Ja | response_format: {"type":"json_object"} |
| Sampling-Parameter | Ja | Temperatur, top_p, Stop, Seed, Strafen |
| Bilder, Audio, Video | Nein | Nur Text |
| Embeddings, Fine-Tuning | Nein | Nicht angeboten |
Das Modell ist ein Open-Weight-Textmodell mit der ID uncensored, betrieben von einem unabhängigen Anbieter. Es antwortet ohne Inhaltsfilter für die legale Nutzung durch Erwachsene und ist keine offizielle API von OpenAI oder einem anderen Anbieter. Jedes Beispiel unten verwendet die Base URL https://api.chatgptunfiltered.com/v1.
Alles, was nicht auf dieser Liste steht, ist als nicht verfügbar zu behandeln, bis die Dokumentation etwas anderes besagt. Dazu gehören herstellerspezifische gehostete Tools, die sich vom unten gezeigten Function Calling unterscheiden.
Streaming
Setze stream: true und iteriere über die Chunks. Gib die Textstücke aus, sobald sie eintreffen, und lies die Nutzung aus dem letzten 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)
Zwei Dinge, die du beachten solltest. Erstens belegt eine gestreamte Anfrage ihren Concurrency-Slot, bis der Stream endet, sodass ein abgebrochener Stream einen Slot blockiert; schließe ihn, wenn der Nutzer die Seite verlässt. Zweitens kann der letzte Chunk Nutzungsinformationen enthalten, aber keinen Text, also schütze den Zugriff auf choices wie oben gezeigt.
In einer Web-App leite die Chunks an den Browser weiter, sobald sie eintreffen, zum Beispiel über deine eigene SSE-Route, und lass die Seite jedes Textstück an die Nachricht anhängen. Führe die Nutzungsbuchhaltung auf dem Server durch, sobald der Stream geschlossen ist, wo der letzte Chunk dir die Kosten der Anfrage mitteilt.
Tool-Calling, End-to-End
Definiere eine Funktion, lass das Modell sie anfordern, führe sie selbst aus und sende das Ergebnis mit role: "tool" zurück.
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)
Das Modell entscheidet, ob die Funktion aufgerufen wird; tool_choice ermöglicht es dir, das zu steuern. Die Funktion selbst wird in deinem Code ausgeführt, niemals auf dem Server, sodass du kontrollierst, worauf sie zugreifen kann. Validiere die Argumente, bevor du darauf reagierst, da das Modell einen falschen Typ oder ein fehlendes Feld senden kann.
Gestreamte Tool-Aufrufe
Wenn du stream: true mit tools kombinierst, kommt der Aufruf in Stücken an. Der Funktionsname kommt zuerst, und der Argument-String kommt fragmentiert an, den du nach index verknüpfen musst.
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"]))
Parst args erst, nachdem der Stream beendet ist. Eine halbe Zeichenkette ist kein gültiges JSON. Danach führe die Funktion aus und sende das Ergebnis genau wie im Nicht-Streaming-Beispiel zurück.
JSON-Modus
Für strukturierte Antworten aktiviere den JSON-Modus und beschreibe die gewünschte Form im 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)
JSON-Modus macht gültiges JSON viel wahrscheinlicher, erzwingt aber dein Schema nicht. Prüfe die Schlüssel und Typen selbst und halte einen Wiederholungspfad bereit. Niedrige Temperatur hilft bei der Extraktion. Sampling-Einstellungen wie temperature, top_p, stop, seed und die Penalties werden durchgereicht.
Ein nützliches Muster ist die Kombination des JSON-Modus mit einer kleinen Validierungsfunktion, die eine klare Fehlermeldung zurückgibt, und die Rückmeldung dieser Nachricht an das Modell bei einem einzelnen Retry: „Deine letzte Antwort fehlte der Schlüssel intensity. Antworte erneut mit gültigem JSON.“ Modelle korrigieren sich meist beim zweiten Versuch, und die zusätzliche Anfrage kostet nur einen Bruchteil eines Cents.
Fehlerbehandlung während eines Streams
Streams schlagen auf andere Weise fehl als normale Anfragen: Die Verbindung kann nach der Hälfte des Textes abbrechen. Plane das ein.
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")
Eine 429 hier bedeutet ein Limit oder eine temporäre Überlastung, und der richtige Schritt ist, 5 Sekunden zu warten, was dem Retry-After: 5 entspricht, das die API sendet. Wenn ein Stream mitten abbricht, hast du eine teilweise Antwort. Entscheide, ob du sie verwerfen und wiederholen sollst, oder sie behalten und das Modell bitten sollst, fortzufahren. Für Chat-Oberflächen ist das Verwerfen und Wiederholen für Nutzer normalerweise weniger verwirrend.
Limits und Kosten zur Planung
- Rate: 300 Anfragen pro Minute auf einem Voll-Key, 60 auf einem Trial-Key.
- Concurrency: 8 auf einem Voll-Key, 2 auf einem Trial-Key. Denk daran, dass jeder offene Stream zählt.
- Größe: Anfragekörper bis zu 8 MB; Kontextfenster von 100.000 Token, Prompt plus Completion.
- 429-Handling: Codes
rate_limitedundconcurrencybedeuten, dass du ein Limit überschritten hast; temporäre Überlastung gibt 429 mitRetry-After: 5zurück.
Kostenbeispiel: Ein Tool-Calling-Austausch mit zwei Anfragen, 1.200 Token Input und 80 Output für die erste, 1.400 Input und 150 Output für die zweite. Input ist 2.600 x 0,15 $ / 1 Mio. = 0,00039 $. Output ist 230 x 0,50 $ / 1 Mio. = 0,000115 $. Gesamt 0,000505 $. Fehlgeschlagene oder abgelehnte Anfragen werden nicht berechnet. Die Preise stehen auf Preisen, und die vollständige Methodensliste findest du in den Dokumenten. Starte mit dem 0,50-$-Testguthaben von der API-Schlüssel-Seite. Für Hintergrundinformationen zum Modell sieh dir Verständnis unzensierter ChatGPT-Modelle an. Die harte Grenze gilt in jedem Modus: keine sexuellen Inhalte mit Minderjährigen.
Eine Pre-Launch-Checkliste
- Tool-Argumente werden mit
json.loadsin einem try-Block geparst und gegen die erwarteten Schlüssel und Typen geprüft, bevor irgendetwas ausgeführt wird. - Die Funktionen, die du freigibst, können nur das, was du einem anonymen Nutzer erlauben würdest. Das Modell entscheidet, wann es sie aufruft, nicht du.
- JSON-Antworten werden validiert, mit einem Retry im Fehlerfall.
- Streams werden geschlossen, wenn der Nutzer die Seite verlässt, also belegen sie keinen Concurrency-Slot mehr.
- Dein Concurrency-Limit im Code liegt bei oder unter dem Limit des Schlüssels: 8 für einen vollständigen Schlüssel.
- Request-IDs von
X-Request-Idwerden geloggt. - Der Key lebt auf dem Server und kann regeneriert werden; der alte Key funktioniert sofort nicht mehr.
Der Dienst ist nur Text, also muss ein Feature, das ein Bild oder ein Embedding benötigt, von einem anderen Anbieter kommen.
