DE ▾
Jetzt starten

ChatGPT Unfiltered API: Streaming, Tools und JSON

Eine ChatGPT Unfiltered API ist nur nützlich, wenn sie unterstützt, was echte Apps brauchen: gestreamten Text, Tool-Aufrufe und strukturierte Ausgaben. Dieser Endpunkt unterstützt alle drei im OpenAI-Format. Unten findest du funktionierenden Code für jede Funktion und eine klare Liste der nicht unterstützten Features.

Zuletzt aktualisiert

Ein Strom von Lichtpartikeln auf einem Förderband, wie ein Roboterarm strukturierte Blöcke aufnimmt, für Streaming-Tools und JSON

Zusammenfassung

  1. Unterstützt: Streaming über SSE, Tool-Calling mit gestreamten tool_calls, JSON-Modus und die üblichen Sampling-Einstellungen.
  2. Nicht unterstützt: Bild-, Audio- oder Video-Generierung, Embeddings, Fine-Tuning.
  3. Streams belegen einen parallelen Anfrage-Slot bis zum Ende: 8 Slots auf einem Voll-Key, 2 auf einem Trial-Key.
  4. Parst Tool-Argumente und JSON-Antworten defensiv; Modelle können fehlerhafte Ausgaben liefern.

Was unterstützt wird und was nicht

MerkmalStatusHinweise
Chat-VervollständigungenJaPOST /v1/chat/completions
AntwortenJaPOST /v1/responses
Streaming (SSE)Jastream: true, mit Nutzung am Ende
Function CallingJatools, tool_choice, gestreamte tool_calls, Ergebnisse als role: "tool"
JSON-ModusJaresponse_format: {"type":"json_object"}
Sampling-ParameterJaTemperatur, top_p, Stop, Seed, Strafen
Bilder, Audio, VideoNeinNur Text
Embeddings, Fine-TuningNeinNicht 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_limited und concurrency bedeuten, dass du ein Limit überschritten hast; temporäre Überlastung gibt 429 mit Retry-After: 5 zurü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.loads in 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-Id werden 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.

Häufig gestellte Fragen

Streamt der Endpunkt Antworten?

Ja. Sende stream: true, um Server-Sent Events zu erhalten, wobei die Nutzungsinformationen am Ende des Streams gemeldet werden.

Unterstützt es Function Calling?

Ja. Verwende tools und tool_choice, lese tool_calls aus der Antwort und sende Ergebnisse als Nachrichten mit der Rolle tool zurück. Auch gestreamte tool_calls werden unterstützt.

Gibt der JSON-Modus immer gültiges JSON zurück?

Es macht gültiges JSON viel wahrscheinlicher, ist aber keine Garantie und erzwingt kein Schema. Analysiere die Antwort innerhalb eines try-Blocks und wiederhole den Vorgang bei einem Fehler.

Kann ich Streaming und Tools in derselben Anfrage verwenden?

Ja. Tool-Aufrufnamen und Argumente kommen fragmentiert an, daher verbinde sie nach Index und analysiere die Argumente nach Ende des Streams.

Erhalte deinen API-Schlüssel

Melde dich an, kopiere den Token, aktualisiere deine Base URL. Das ist alles.

Jetzt starten