RU ▾
Начать сейчас

API ChatGPT без цензуры: потоковая передача, инструменты и JSON

API ChatGPT без цензуры полезен только при поддержке того, что нужно реальным приложениям: потоковый текст, вызовы инструментов и структурированный вывод. Этот эндпоинт поддерживает все три функции в формате OpenAI. Ниже приведен рабочий код для каждого случая и четкий список того, что не поддерживается.

Последнее обновление

Поток светящихся частиц на конвейере, где роботизированная рука собирает структурированные блоки, для потоковой передачи инструментов и JSON

Итоги

  1. Поддерживается: потоковая передача через SSE, вызов инструментов с потоковыми tool_calls, JSON-режим и стандартные настройки сэмплирования.
  2. Не поддерживается: генерация изображений, аудио или видео, эмбеддинги, дообучение (fine-tuning).
  3. Потоки занимают слот параллельных запросов до завершения: 8 слотов для полного ключа, 2 для пробного ключа.
  4. Парсите аргументы инструментов и ответы JSON с защитой от ошибок; модели могут возвращать некорректный вывод.

Что поддерживается и что нет

ВозможностьСтатусПримечания
Завершение чатаДаPOST /v1/chat/completions
ОтветыДаPOST /v1/responses
Потоковая передача (SSE)Даstream: true, с информацией об использовании в конце
Вызов функцийДаtools, tool_choice, потоковая передача tool_calls, результаты в виде role: "tool"
JSON-режимДаresponse_format: {"type":"json_object"}
Управление сэмплированиемДаtemperature, top_p, stop, seed, штрафы
Изображения, аудио, видеоНетТолько текст
Эмбеддинги, дообучениеНетНе предлагается

Модель представляет собой одну текстовую модель с открытыми весами с идентификатором uncensored, управляемую независимым провайдером. Она отвечает без отказов по контенту для законного использования взрослыми, и это не официальный API OpenAI или любого другого поставщика. Каждый пример ниже использует базовый URL https://api.chatgptunfiltered.com/v1.

Все, что не указано в этом списке, следует считать недоступным, пока в документации не будет указано иное. Это включает в себя специфичные для поставщика хостинговые инструменты, которые отличаются от вызова функций, показанного ниже.

Потоковая передача

Установите stream: true и перебирайте фрагменты. Выводите текстовые фрагменты по мере их поступления и читайте информацию об использовании из последнего фрагмента.

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)

Запомните две вещи. Во-первых, потоковый запрос занимает слот параллельных запросов до завершения потока, поэтому оставленный поток держит слот занятым; закрывайте его, когда пользователь уходит. Во-вторых, последний фрагмент может содержать информацию об использовании, но не содержать текста, поэтому защищайте доступ к choices, как показано выше.

В веб-приложении передавайте фрагменты браузеру по мере их поступления, например, через ваш собственный маршрут SSE, и позволяйте странице добавлять каждый фрагмент текста к сообщению. Ведите учет использования на сервере после закрытия потока, где последний фрагмент сообщает о стоимости запроса.

Вызов инструментов от начала до конца

Объявите функцию, позвольте модели запросить её, выполните её самостоятельно и отправьте результат обратно с 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)

Модель решает, вызывать ли функцию; tool_choice позволяет управлять этим. Сама функция выполняется в вашем коде, а не на сервере, поэтому вы контролируете, к чему она может получить доступ. Проверяйте аргументы перед выполнением, так как модель может отправить неверный тип или пропущенное поле.

Потоковая передача вызовов инструментов

При объединении stream: true с tools вызов поступает частями. Имя функции приходит первым, а строка аргументов поступает фрагментами, которые нужно объединить по 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"]))

Парсите args только после завершения потока. Половина полученной строки не является корректным JSON. После этого выполните функцию и отправьте результат обратно точно так же, как в примере без потоковой передачи.

JSON-режим

Для структурированных ответов включите JSON-режим и опишите желаемую форму в промпте.

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-режим значительно повышает вероятность получения корректного JSON, но не принудительно применяет вашу схему. Проверяйте ключи и типы самостоятельно и предусмотрите путь для повторных попыток. Низкая температура помогает при извлечении данных. Настройки сэмплирования, такие как temperature, top_p, stop, seed и штрафы, передаются как есть.

Полезный паттерн — объединить JSON-режим с небольшой функцией проверки, которая возвращает понятное сообщение об ошибке, а затем передать это сообщение модели для одной повторной попытки: "В вашем последнем ответе отсутствовал ключ intensity. Повторите ответ с корректным JSON." Модели обычно исправляют себя при второй попытке, а дополнительный запрос стоит доли цента.

Обработка сбоев во время потоковой передачи

Потоки завершаются с ошибками, которые не возникают в обычных запросах: соединение может разорваться после получения половины текста. Запланируйте это.

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

Код 429 здесь означает лимит или временную перегрузку, и правильное действие — подождать 5 секунд, что соответствует заголовку HTTP Retry-After: 5, который отправляет API. Если поток прерывается на полпути, у вас есть частичный ответ. Решите, отбросить его и повторить запрос, или сохранить его и попросить модель продолжить. Для чат-интерфейсов отбрасывание и повторный запрос обычно менее запутывают пользователей.

Лимиты и стоимость для планирования

  • Скорость: 300 запросов в минуту для полного ключа, 60 для пробного ключа.
  • Параллельные запросы: 8 для полного ключа, 2 для пробного ключа. Помните, что каждый открытый поток учитывается.
  • Размер: тела запросов до 8 МБ; контекстное окно на 100 000 токенов, включая промпт и завершение.
  • Обработка 429: коды rate_limited и concurrency означают, что вы превысили лимит; временная перегрузка возвращает 429 с заголовком Retry-After: 5.

Пример стоимости: обмен с вызовом функций с двумя запросами, 1 200 токенов на входе и 80 на выходе для первого, 1 400 на входе и 150 на выходе для второго. Вход: 2 600 × $0,15 / 1M = $0,00039. Выход: 230 × $0,50 / 1M = $0,000115. Итого $0,000505. Неудачные или отклонённые запросы не оплачиваются. Тарифы указаны на странице цен, а полный список методов — в документации. Начните с пробного баланса $0,50 на странице API-ключей. Для справки о модели см. понимание моделей ChatGPT без цензуры. Жёсткое ограничение действует во всех режимах: запрещён сексуальный контент с участием несовершеннолетних.

Чек-лист перед запуском

  • Аргументы инструментов парсятся с помощью json.loads в блоке try и проверяются на соответствие ожидаемым ключам и типам перед выполнением любых действий.
  • Функции, которые вы предоставляете, могут делать только то, что вы разрешили бы сделать анонимному пользователю. Модель решает, когда их вызывать, а не вы.
  • Ответы JSON проверяются, с одной попыткой повторного запроса при ошибке.
  • Потоки закрываются, когда пользователь покидает страницу, поэтому они не занимают слот параллельных запросов.
  • Ваш предел параллельных запросов в коде равен или ниже лимита ключа: 8 для полного ключа.
  • Идентификаторы запросов из X-Request-Id логируются.
  • Ключ хранится на сервере и может быть сгенерирован заново; старый ключ перестаёт работать сразу.

Сервис работает только с текстом, поэтому функция, требующая изображения или эмбеддинга, должна предоставляться другим провайдером.

Часто задаваемые вопросы

Поддерживает ли эндпоинт потоковую передачу ответов?

Да. Отправьте stream: true, чтобы получать события SSE, с отчётом об использовании в конце потока.

Поддерживает ли он вызов функций?

Да. Используйте tools и tool_choice, считывайте tool_calls из ответа и возвращайте результаты в виде сообщений с role: tool. Поддерживается потоковая передача tool_calls.

Всегда ли JSON-режим возвращает валидный JSON?

Это значительно повышает вероятность получения валидного JSON, но не является гарантией, и не накладывает ограничений схемы. Парсите внутри блока try и повторяйте запрос при сбое.

Можно ли использовать потоковую передачу и инструменты в одном запросе?

Да. Имена вызовов инструментов и их аргументы поступают фрагментами; объединяйте их по индексу и парсите аргументы после завершения потока.

Получите API-ключ

Зарегистрируйтесь, скопируйте токен, обновите базовый URL. Вот и всё.

Начать сейчас