ShvIA

Docs · API compatível com Anthropic

Messages

O dialeto Messages da Anthropic. Use com ferramentas e SDKs que só falam essa API, principalmente o Claude Code. Qualquer modelo do gateway pode responder, não só os modelos da Anthropic: o gateway traduz a requisição.

POST/v1/messages#

  • Auth: a sua chave de API em x-api-key ou Authorization: Bearer. Os headers anthropic-version e anthropic-beta são aceitos e ignorados.
  • Limite: 20 requisições por minuto por conta por padrão, um contador que todos os endpoints /v1 dividem com os outros endpoints que têm um número por minuto próprio. Veja limites e cotas.
  • Corpo: JSON.
bash
curl -s "$SHVIA_URL/v1/messages" \
  -H "x-api-key: $SHVIA_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "model": "'"$SHVIA_MODEL"'",
    "max_tokens": 256,
    "messages": [{ "role": "user", "content": "Say hello in one short sentence." }]
  }'

Requisição#

CampoNotas
modelobrigatório. Um id de modelo de modelos. Um nome real de modelo da Anthropic não é mapeado. Se ele não está na sua lista e não tem @server, não é recusado: o servidor padrão responde com outro modelo, então confira model na resposta. Com um @server explícito que o gateway não tem, você recebe 404
messagesobrigatório, de 1 a 200 mensagens com role user ou assistant. content é uma string ou uma lista de blocos
systemuma string, ou uma lista de blocos de texto
collectionslista opcional de 1 a 5 ids de suas coleções. Os trechos delas que dizem respeito à última mensagem do usuário são acrescentados a system. Não faz parte da API da Anthropic. As recusas trazem error.code, e os headers X-Inference-Collections e X-Inference-Excerpts dizem o que foi usado
streamtrue para server-sent events
tools, tool_choiceferramentas de função. tool_choice é um objeto cujo type é auto, any, none ou tool (com um name). Uma string solta é ignorada, assim como um tool_choice enviado sem tools
max_tokensopcional aqui, ao contrário da API da Anthropic. Se você enviar, tem de ser um inteiro de pelo menos 1. Não é aplicado

Os blocos de conteúdo entendidos são text, tool_use (numa mensagem do assistente) e tool_result (numa mensagem do usuário).

O que é descartado em silêncio#

Nada abaixo produz erro. A requisição tem sucesso e o item é ignorado:

  • image, document, thinking e qualquer outro tipo de bloco. Uma mensagem feita só desses blocos some da conversa, e uma requisição que só tem mensagens assim pode ser recusada pelo modelo.
  • temperature, top_p, top_k, stop_sequences, thinking, metadata, cache_control, e todo outro campo de nível superior.
  • Ferramentas tipadas, como busca na web ou uma ferramenta de computador, são tratadas como ferramentas de função comuns, pelo nome.

Resposta#

json
{
  "id": "msg_0123456789abcdef01234567",
  "type": "message",
  "role": "assistant",
  "model": "claude-sonnet-4-6@anthropic",
  "content": [{ "type": "text", "text": "Hello! Nice to meet you." }],
  "stop_reason": "end_turn",
  "stop_sequence": null,
  "usage": { "input_tokens": 14, "output_tokens": 8 }
}
  • stop_reason é tool_use quando a resposta traz chamadas de ferramenta, e end_turn nos outros casos. Nunca é max_tokens.
  • Uma chamada de ferramenta é um bloco {"type": "tool_use", "id": "…", "name": "…", "input": {…}}. input é sempre um objeto.
  • usage tem só input_tokens e output_tokens: não há contadores de cache.
  • Como em chat completions, model é o id que o gateway resolveu. Ele não mostra um fallback para o seu modelo padrão.

Streaming#

Com "stream": true a resposta é text/event-stream e cada evento é event: <name> mais data: {…}. Os eventos são exatamente estes:

EventoQuando
message_starto primeiro. usage.input_tokens é sempre 0 aqui
content_block_startum bloco de texto abre com o primeiro texto, e cada chamada de ferramenta abre um bloco
content_block_deltatext_delta para texto; para uma chamada de ferramenta, um input_json_delta que traz os argumentos inteiros
content_block_stopum bloco fecha
message_deltastop_reason e usage.output_tokens
message_stopo último, num fim normal
errorno lugar dos dois últimos, quando a resposta falha
sse
event: message_start
data: {"type":"message_start","message":{"id":"msg_…","type":"message","role":"assistant","model":"claude-sonnet-4-6@anthropic","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":0,"output_tokens":0}}}

event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}

event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}}

event: content_block_stop
data: {"type":"content_block_stop","index":0}

event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"output_tokens":8}}

event: message_stop
data: {"type":"message_stop"}

Não há evento ping. Os tokens de entrada não são informados num stream, então use o usage sem stream ou a contagem de tokens quando precisar deles.

Erros#

Os erros usam o formato da Anthropic {"type": "error", "error": {"type": "…", "message": "…"}}, com duas exceções: um 401 usa o formato da OpenAI, e um 429 é o simples {"message": "Too Many Attempts."} quando você envia Accept: application/json.

Statuserror.typeQuando
400invalid_request_erroro corpo falhou na validação
401o formato da OpenAI, code invalid_api_keya chave não é válida
403permission_erroro modelo não é permitido para o seu grupo, ou uma cota acabou
404not_found_erroro id tem um servidor e o gateway não tem esse modelo lá
429nenhumlimite de requisições atingido. Leia Retry-After
502api_erroro modelo não devolveu nada, ou não pôde ser alcançado
504timeout_erroro modelo parou no meio da resposta. O corpo acrescenta uma mensagem partial, e X-Inference-Incomplete: read_timeout é enviado
o status do provedorapi_errortodos os modelos da cadeia falharam e um provedor recusou a chamada. O status é repassado, e type é api_error seja qual for. Um 401 ou 403 aqui pode vir do provedor do modelo, por exemplo um servidor de modelo que rejeita as credenciais do próprio gateway, e não da sua chave

POST/v1/messages/count_tokens#

Devolve uma estimativa local do tamanho da entrada, para que clientes como o Claude Code decidam quando compactar. Não chama nenhum modelo e não usa cota, mas conta no limite de requisições.

bash
curl -s "$SHVIA_URL/v1/messages/count_tokens" \
  -H "x-api-key: $SHVIA_API_KEY" -H "content-type: application/json" \
  -d '{ "model": "'"$SHVIA_MODEL"'", "messages": [{ "role": "user", "content": "Hello, world" }] }'
json
{ "input_tokens": 3 }

A resposta traz X-Inference-Tokens-Source: estimate-chars-4: o número é a contagem de caracteres dividida por 4, arredondada para cima, e não um tokenizador. Não vai bater com a contagem de nenhum modelo. As chamadas de ferramenta anteriores no histórico não são contadas, então o histórico longo de um agente sai subcontado. O model não é conferido.

Use com o Claude Code#

Aponte o Claude Code para o host, sem /v1: o cliente acrescenta /v1/messages sozinho.

bash
export ANTHROPIC_BASE_URL="https://ai.shvia.org"
export ANTHROPIC_AUTH_TOKEN="$SHVIA_API_KEY"
export ANTHROPIC_MODEL="$SHVIA_MODEL"
claude

Definir ANTHROPIC_BASE_URL como https://ai.shvia.org/v1 faz o cliente requisitar /v1/v1/messages, que não existe. A configuração completa, e as outras ferramentas, estão em conecte suas ferramentas.

Um assistente de código pode enviar várias requisições por turno: a própria mensagem, a contagem de tokens e uma nova tentativa quando uma requisição falha. O limite padrão de 20 requisições por minuto é fácil de atingir com um deles, então peça ao seu administrador que o aumente para uso intenso.

Tratamento de dados#

Como os outros endpoints /v1, nada é guardado como conversa, cada chamada é medida, e identificadores comuns no texto são mascarados antes de ele ir para um modelo que não está na infraestrutura do próprio operador. Veja seus dados.