Inferência compatível com OpenAI
A ReOpenly expõe uma API compatível com OpenAI com escopo de projeto. O modelo disponível pode ser uma base do catálogo, um job concluído ou um alias hospedado ativo. A inferência passa pelo plano de controle até um worker isolado ou uma unidade de serving gerenciada; não existe um caminho de geração simulado apenas para protótipo.
Endpoints
GET /api/projects/{owner_slug}/{project_slug}/openai/v1/models
POST /api/projects/{owner_slug}/{project_slug}/openai/v1/chat/completions
POST /api/projects/{owner_slug}/{project_slug}/openai/v1/completions
O primeiro endpoint retorna somente os modelos que o bearer pode invocar:
{
"object": "list",
"data": [
{
"id": "support-assistant",
"object": "model",
"created": 1764547200,
"owned_by": "reopenly"
}
]
}
Use o id retornado para um alias hospedado. Um modelo do catálogo é
endereçado como base#<catalog-model-id>, por exemplo
base#unsloth/Qwen3.5-9B. Um job concluído pode ser endereçado como
training-job#<job-id> quando o chamador tem acesso ao projeto.
Chat completions
curl "${REOPENLY_OPENAI_BASE}/chat/completions" \
-H "Authorization: Bearer ${REOPENLY_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"model": "base#unsloth/Qwen3.5-9B",
"messages": [
{"role": "user", "content": "Dê uma frase sobre datasets."}
],
"max_tokens": 64,
"temperature": 0.2,
"stream": false
}'
A requisição aceita model, messages, max_tokens, temperature, top_p,
reasoning_effort, funções em tools, stream e
stream_options.include_usage. A resposta é um chat completion OpenAI com
choices, motivo de término e campos de uso:
{
"id": "chatcmpl-…",
"object": "chat.completion",
"created": 1764547200,
"model": "base#unsloth/Qwen3.5-9B",
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": "…"},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 18,
"completion_tokens": 12,
"total_tokens": 30,
"reasoning_tokens": 0,
"cached_tokens": 0
}
}
As contagens vêm do caminho de serving quando disponíveis. Uma contagem ausente do upstream é representada como zero; clientes não devem tratá-la como uma estimativa feita no cliente.
Padrões para campos omitidos
Se um campo opcional for omitido, o servidor aplica:
| Campo | Aplicado quando omitido |
|---|---|
max_tokens | 1024 |
temperature | 1.0 |
top_p | 1.0 |
stream | false |
reasoning_effort | nada — o padrão do próprio modelo |
GET /api/base-models publica a mesma tabela em um bloco inference
(defaults e capabilities), então o cliente pode lê-la em vez de fixá-la no
código. O playground da primeira parte se inicializa a partir desse bloco, e é
isso que faz um prompt digitado lá e o mesmo prompt enviado por esta API serem
a mesma requisição.
reasoning_effort merece atenção: none é uma instrução explícita para
desligar o raciocínio, enquanto omitir o campo pede ao modelo o que ele faz por
padrão. São requisições diferentes. Os valores explícitos são none, low e
high; o playground oferece uma quarta opção, "padrão do modelo", que não
envia campo nenhum.
Streaming
Defina stream: true para receber frames text/event-stream. Streams bem
solicitados terminam com um chunk de finalização seguido de:
data: [DONE]
Solicite o chunk final de uso padrão quando necessário:
{
"model": "support-assistant",
"messages": [{"role": "user", "content": "Resuma isto."}],
"stream": true,
"stream_options": {"include_usage": true}
}
O frame de uso tem choices vazio e um objeto usage. Não presuma que todo
frame de texto tenha role ou conteúdo: deltas de tool call e o frame de
finalização têm formatos diferentes.
Se a requisição falhar antes do início do stream HTTP, o servidor retorna o
erro JSON normal. Se o worker falhar depois de 200 OK e dos headers SSE, o
frame final é um envelope de erro JSON e o stream termina sem [DONE]. Veja
Erros e tentativas.
Completions legados
POST /completions aceita uma string ou uma lista de strings em prompt e
retorna uma resposta OpenAI text_completion. Aceita model, prompt,
max_tokens, temperature, top_p e reasoning_effort. O contrato atual
desse endpoint não oferece streaming.
Limites de contexto
Há duas verificações:
- A verificação do catálogo usa
max_context_tokensdeGET /api/base-models. Em produção, o Qwen3.5 9B anuncia 4096. - Uma unidade vLLM faz uma verificação exata antes da admissão usando seu
próprio
max_model_len.
A verificação do catálogo é conservadora e ocorre antes do dispatch. Portanto,
uma requisição pode falhar com invalid_request mesmo quando um worker parece
ter GPU livre. Consulte o catálogo do deployment e reserve espaço para
max_tokens; nunca fixe um limite baseado nesta página.
Cobrança e tentativas
As requisições são cobradas conforme o caminho de serving somente depois que se
confirma a execução do dispatch. Toda requisição bem-sucedida em uma rota com
tarifa positiva tem duração mínima cobrável de um segundo na tarifa congelada
para aquela requisição (0,0005 crédito quando a tarifa é 0,03 crédito por
minuto de GPU). Rotas explicitamente gratuitas continuam grátis, e requisições
com falha não são cobradas. Uma tentativa automática só é segura quando a
resposta traz model_warming e Retry-After. Uma resposta
ambiguous_transport pode significar que a requisição foi atendida; repetir
às cegas pode duplicar trabalho e cobrança. O playground v2 mantém a conversa
original, faz no máximo três novas tentativas automáticas após a requisição
original e deixa uma ação manual de Tentar novamente quando o limite acaba.
Ele nunca repete um erro durante o streaming.
O playground global guarda o escopo do projeto na URL:
/app/playground?project=owner/project. Ele ainda chama o mesmo endpoint
OpenAI com escopo de projeto descrito acima; não existe uma rota de inferência
sem escopo.