Pular para o conteúdo principal

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:

CampoAplicado quando omitido
max_tokens1024
temperature1.0
top_p1.0
streamfalse
reasoning_effortnada — 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:

  1. A verificação do catálogo usa max_context_tokens de GET /api/base-models. Em produção, o Qwen3.5 9B anuncia 4096.
  2. 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.