O que é suportado e o que não é
| Recurso | Status | Notas |
|---|---|---|
| Completions de chat | Sim | POST /v1/chat/completions |
| Respostas | Sim | POST /v1/responses |
| Streaming (SSE) | Sim | stream: true, com uso no final |
| Chamada de ferramentas | Sim | tools, tool_choice, streaming de tool_calls, resultados como role: "tool" |
| Modo JSON | Sim | response_format: {"type":"json_object"} |
| Controles de amostragem | Sim | temperature, top_p, stop, seed, penalidades |
| Imagens, áudio, vídeo | Não | Apenas texto |
| Embeddings, fine-tuning | Não | Nã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_limitedeconcurrencysignificam que você ultrapassou um limite; sobrecarga temporária retorna 429 comRetry-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.loadsem 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-Idsã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.
