PT ▾
Começar agora

API ChatGPT sem censura: streaming, ferramentas e JSON

Uma API ChatGPT sem censura só é útil se suportar o que apps reais precisam: texto em streaming, chamadas de ferramentas e saída estruturada. Este endpoint faz tudo isso no formato OpenAI. Abaixo está código funcional para cada caso e uma lista clara do que não é suportado.

Última atualização

Um fluxo de partículas de luz em uma esteira com um braço robótico pegando blocos estruturados, para streaming de ferramentas e JSON

Resumo

  1. Suportado: streaming via SSE, chamada de ferramentas com tool_calls em streaming, modo JSON e as configurações de amostragem usuais.
  2. Não suportado: geração de imagem, áudio ou vídeo, embeddings, ajuste fino.
  3. Os streams ocupam uma vaga de concorrência até o fim: 8 vagas em uma chave completa, 2 em uma chave de teste.
  4. Analise argumentos de ferramentas e respostas JSON de forma defensiva; modelos podem retornar saída malformada.

O que é suportado e o que não é

RecursoStatusNotas
Completions de chatSimPOST /v1/chat/completions
RespostasSimPOST /v1/responses
Streaming (SSE)Simstream: true, com uso no final
Chamada de ferramentasSimtools, tool_choice, streaming de tool_calls, resultados como role: "tool"
Modo JSONSimresponse_format: {"type":"json_object"}
Controles de amostragemSimtemperature, top_p, stop, seed, penalidades
Imagens, áudio, vídeoNãoApenas texto
Embeddings, fine-tuningNãoNão oferecido

O modelo é um modelo de texto de pesos abertos com o ID uncensored, executado por um provedor independente. Ele responde sem recusas de conteúdo para uso adulto lícito e não é uma API oficial da OpenAI ou de qualquer outro fornecedor. Cada exemplo abaixo usa a URL base https://api.chatgptunfiltered.com/v1.

Qualquer coisa que não esteja nesta lista deve ser tratada como indisponível até que a documentação indique o contrário. Isso inclui ferramentas hospedadas específicas de fornecedores, que são diferentes da chamada de funções mostrada abaixo.

Streaming

Defina stream: true e itere sobre os pedaços. Imprima os trechos de texto conforme chegam e leia o uso do último pedaço.

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)

Duas coisas para lembrar. Primeiro, uma requisição em streaming ocupa seu slot de concorrência até o fim do streaming, então um streaming abandonado mantém um slot ocupado; feche-o quando o usuário sair. Segundo, o último chunk pode conter uso e nenhum texto, então proteja o acesso a choices como acima.

Em um app web, encaminhe os chunks para o navegador conforme chegam, por exemplo via sua própria rota SSE, e deixe a página anexar cada trecho de texto à mensagem. Faça a contabilidade de uso no servidor assim que o streaming fechar, onde o último chunk informa o custo da requisição.

Chamada de ferramentas, do início ao fim

Declare uma função, deixe o modelo solicitá-la, execute-a você mesmo e envie o resultado de volta com 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)

O modelo decide se chama a função; tool_choice permite direcionar isso. A função é executada no seu código, nunca no servidor, então você controla o que ela pode acessar. Valide os argumentos antes de agir, pois um modelo pode enviar um tipo errado ou um campo ausente.

Chamadas de ferramentas em streaming

Quando você combina stream: true com tools, a chamada chega em pedaços. O nome da função vem primeiro, e a string de argumentos chega em fragmentos que você deve unir por 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"]))

Analise apenas args após o streaming terminar. Uma string recebida pela metade não é JSON válido. Depois disso, execute a função e envie o resultado de volta exatamente como no exemplo sem streaming.

Modo JSON

Para respostas estruturadas, ative o modo JSON e descreva a forma desejada no 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)

O modo JSON torna o JSON válido muito mais provável, mas não impõe seu schema. Verifique as chaves e tipos você mesmo e mantenha um caminho de retry. Temperatura baixa ajuda na extração. Configurações de amostragem como temperature, top_p, stop, seed e as penalidades são passadas diretamente.

Um padrão útil é combinar modo JSON com uma pequena função de validação que retorna uma mensagem de erro clara, então envie essa mensagem de volta ao modelo em um único retry: "Sua última resposta estava faltando a chave intensity. Responda novamente com JSON válido." Modelos geralmente se corrigem na segunda tentativa, e a requisição extra custa uma fração de centavo.

Tratamento de falhas durante um streaming

Streams falham de maneiras que requisições simples não fazem: a conexão pode cair após metade do texto. Planeje para isso.

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

Um 429 aqui significa um limite ou sobrecarga temporária, e a ação correta é aguardar 5 segundos, o que corresponde ao Retry-After: 5 que a API envia. Se um streaming quebrar pela metade, você tem uma resposta parcial. Decida se descarta e tenta novamente, ou mantém e pede ao modelo para continuar. Para UIs de chat, descartar e tentar novamente geralmente é menos confuso para os usuários.

Limites e custo para planejar

  • Taxa: 300 solicitações por minuto para uma chave completa, 60 para uma chave de avaliação.
  • Concorrência: 8 para uma chave completa, 2 para uma chave de avaliação. Lembre-se de que cada stream aberto conta.
  • Tamanho: corpos de requisição até 8 MB; janela de contexto de 100.000 tokens, prompt mais conclusão.
  • Tratamento 429: códigos rate_limited e concurrency significam que você ultrapassou um limite; sobrecarga temporária retorna 429 com Retry-After: 5.

Exemplo de custo: uma troca com chamada de funções com duas requisições, 1.200 tokens de entrada e 80 de saída para a primeira, 1.400 de entrada e 150 de saída para a segunda. Entrada é 2.600 x $0,15 / 1M = $0,00039. Saída é 230 x $0,50 / 1M = $0,000115. Total $0,000505. Requisições falhas ou recusadas não são cobradas. Taxas estão em preços, e a lista completa de métodos está na documentação. Comece com o crédito de teste de $0,50 na página da chave de API. Para o contexto do modelo, veja entendendo modelos ChatGPT sem censura. O limite rígido se aplica em todos os modos: é proibido conteúdo sexual envolvendo menores de idade.

Uma lista de verificação pré-lançamento

  • Argumentos de ferramentas são analisados com json.loads em um bloco try, e verificados contra as chaves e tipos esperados antes de qualquer execução.
  • As funções que você expõe podem fazer apenas o que você permitiria que um usuário anônimo fizesse. O modelo escolhe quando chamá-las, não você.
  • Respostas JSON são validadas, com um retry em caso de falha.
  • Streams são fechados quando o usuário sai da página, para que não ocupem um slot de concorrência.
  • Seu limite de concorrência no código está no ou abaixo do limite da chave: 8 para uma chave completa.
  • IDs de requisição de X-Request-Id são registrados.
  • A chave vive no servidor e pode ser regenerada; a antiga para de funcionar imediatamente.

O serviço é apenas texto, então um recurso que precisa de imagem ou embedding deve vir de outro provedor.

Perguntas frequentes

O endpoint transmite respostas?

Sim. Envie stream: true para receber Server-Sent Events, com o uso reportado no final do streaming.

Suporta chamada de funções?

Sim. Use tools e tool_choice, leia tool_calls na resposta e envie os resultados de volta como mensagens com role tool. tool_calls em streaming também são suportados.

O modo JSON sempre retorna JSON válido?

Torna o JSON válido muito mais provável, mas não é uma garantia, e não impõe um esquema. Analise dentro de um bloco try e tente novamente em caso de falha.

Posso usar streaming e ferramentas na mesma requisição?

Sim. Os nomes e argumentos das chamadas de função chegam em fragmentos, então junte-os por índice e analise os argumentos após o fim do streaming.

Obtenha sua chave de API

Crie uma conta, copie o token, atualize sua URL base. Isso é tudo.

Comece agora