Что поддерживается и что нет
| Возможность | Статус | Примечания |
|---|---|---|
| Завершение чата | Да | 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логируются. - Ключ хранится на сервере и может быть сгенерирован заново; старый ключ перестаёт работать сразу.
Сервис работает только с текстом, поэтому функция, требующая изображения или эмбеддинга, должна предоставляться другим провайдером.
