Documentação do app v1.0

Provedores upstream

Os 20 provedores que o Load Balance sabe falar, o que o proxy faz para escolher um deles e o que muda entre eles no comportamento que você enxerga: header enviado, formato do corpo, streaming e janela de uso.

Você fala Anthropic, o LB fala o idioma de cada um

Seu cliente sempre envia POST /v1/messages. Quando o destino não é Anthropic nativo, o LB converte o corpo para o formato do upstream e reconverte a resposta — você continua recebendo Anthropic, inclusive o SSE. A conversão é opcional por provider, e é isso que a seção formatos e conversão descreve.

Pré-requisitos#

Para escolher um provedor você precisa de três coisas:

  • Um cliente que fale a API Messages da Anthropic — Claude Code, ou qualquer outro. O LB não expõe o formato de cada upstream.
  • Uma chave de serviço do LB (sk-lb-…) para o seu cliente apontar para cá. O Quickstart do proxy mostra como emitir e configurar.
  • Uma chave upstream cadastrada em /app/keys, com o provider certo. Essa tela é ADMIN-only — veja URLs e grupos para como a chave entra no rodízio.
  • Dois provedores merecem atenção na baseUrl: o z-ai vem com defaultBaseUrl vazio de propósito, porque existem duas superfícies distintas e você escolhe na baseUrl. E o openai-codex aponta para https://chatgpt.com/backend-api/codex — o endpoint da assinatura do ChatGPT, não a Responses API pública.

    O authType é parte da identidade da chave, não um detalhe de cadastro: ele entra na resolução do provider e decide quais efeitos o adapter aplica. São oito valores — api_key, bearer, claude_cloud, oauth_max, oauth_console, codex_oauth, github e github-copilot.

    A família de provedores#

    Os 20 adapters se auto-registram por efeito colateral do import — cada arquivo chama registerProvider(new XProvider()) no fim, e o barrel backend/src/providers/index.ts importa os 20 na ordem. Não existe lista manual para manter, e o entity_list abaixo segue a ordem do próprio barrel.

    Os que você mais vai usar

    Anthropic anthropic
    Claude nativo nas duas direções: nenhum byte do corpo é convertido.
    x-api-key /v1/messages OAuth api_key
    OpenAI Codex openai-codex
    Plano ChatGPT por device-code OAuth, servido na Responses API.
    codex_oauth /responses janela 5h
    OpenAI openai
    Fallback da resolução e superfície OpenAI-compat mais comum.
    bearer /chat/completions
    OpenRouter openrouter
    Gateway multi-modelo: Claude sai em /v1/messages, o resto em OpenAI.
    api_key /v1/messages /v1/chat/completions
    Google Gemini gemini
    Duas superfícies na mesma chave: compat OpenAI ou :generateContent nativo.
    bearer x-goog-api-key :generateContent
    Minimax minimax
    Token Plan com janela de 5h e semanal, consultada por probe.
    api_key anthropic-version janela 5h
    Muse Spark sobre o protocolo Anthropic nativo, com plano de assinatura.
    bearer /v1/messages plano
    OpenCode Zen opencode
    Zen e Go na mesma URL; o protocolo sai do prefixo do modelo.
    bearer 4 protocolos Zen / Go
    Z.AI z-ai
    GLM: mirror Anthropic em /api/anthropic, OpenAI-compat em /api/paas/v4.
    api_key mirror / compat cache implícito
    Token Plan SGP com header de auth próprio e 1M de contexto.
    api-key /v1/chat/completions
    GitHub Copilot github-copilot
    OAuth por device code, com renovação automática antes da requisição.
    github-copilot Copilot-Integration-Id

    Os demais

    GitHub Models github-models
    Marketplace do GitHub autenticado por PAT.
    github /chat/completions
    Mistral AI mistral
    OpenAI-compat com conversor de resposta próprio.
    bearer /v1/chat/completions
    DashScope compatible-mode, com stripper de campos antes do conversor.
    bearer /chat/completions
    NVIDIA NIM nvidia
    integrate.api.nvidia.com com credencial no formato nvapi-.
    bearer /chat/completions
    Morph LLM morph
    Endpoint de morphllm.com para coding agents.
    bearer /chat/completions
    Morph Fast Models morph-fast-models
    Mesmo endpoint do Morph, catálogo de latência declarada.
    bearer catálogo /chat/completions
    Poolside poolside
    Modelos Laguna de coding agent, tier free, cache implícito.
    bearer /chat/completions
    AgentRouter agentrouter
    Anthropic nativo com fingerprint de Claude Code no pedido.
    api_key /v1/messages fingerprint
    xAI Grok xai
    Grok em /v1/chat/completions, com teto de saída por modelo.
    bearer /chat/completions

    Como o proxy sabe qual é o provedor#

    A resolução acontece em resolveProvider(providerName?, authType?, baseUrl?), em backend/src/providers/registry.ts. A ordem é fixa e o primeiro critério que casar vence:

  • Nome exato do provider. Se a chave tem providerName e ele está registrado, acabou — nenhum outro critério é consultado.
  • authType. github-copilot resolve para github-copilot; github para github-models; codex_oauth para openai-codex; e oauth_max, oauth_console ou claude_cloud para anthropic.
  • Heurística de baseUrl. Duas estratégias, e a diferença importa: substring crua na URL para a maioria — minimax.io, openrouter.ai, generativelanguage.googleapis.com, api.anthropic.com, chatgpt.com/backend-api/codex, opencode.ai, xiaomimimo.com, morphllm.com, api.mistral.ai, inference.poolside.ai, agentrouter.org, dashscope*.aliyuncs.com — e casamento por sufixo de host para z.ai, meta.ai e x.ai.
  • Fallback openai. Se nada casou, o provider é openai. O único throw do arquivo acontece se nem o openai estiver registrado.
  • Por que três domínios usam sufixo de host e o resto usa substring

    baseUrl.includes('z.ai') casaria buzz.ai e jazz.ai, que contêm a substring — e rotearia essas chaves para a Z.AI. O mesmo vale para meta.ai (someta.ai, beta.aihost.com) e para x.ai. Por isso os três usam hostMatches(hostOf(baseUrl), domain): o domínio tem que ser o host exato ou um subdomínio dele. Uma URL sem protocolo é normalizada antes do parse, e uma URL que não parseia devolve host vazio — e host vazio não casa com nada.

    O que muda entre eles#

    A tabela lista só os quirks que você sente: a forma do auth e a diferença de comportamento que muda o que chega no upstream ou o que volta para o seu cliente. Vários providers OpenAI-compat — openai, morph, morph-fast-models, nvidia, poolside, xai — só fazem o básico: convertem x-api-key para Bearer e apagam anthropic-version e anthropic-beta. Por isso não têm linha própria.

    ProvedorForma do authO que muda no comportamento
    anthropicx-api-key; Authorization: Bearer em oauth_max e oauth_consoleCom OAuth, o fingerprint do Claude Code entra no pedido: o anthropic-beta canônico é unido ao que o cliente já mandou e a URL ganha ?beta=true. Para listar modelos o header vira Bearer, porque o upstream devolveria 401 no slot x-api-key.
    openai-codexcodex_oauth por device code; sem header de chaveApaga x-api-key, anthropic-version e anthropic-beta, porque o endpoint de assinatura não é a Responses API pública. Também força stream: true e rebufferiza a transcrição para quem pediu não-streaming.
    opencodeAuthorization: Bearer no chat/responses; x-api-key no messages; x-goog-api-key no geminiApaga Authorization, authorization, x-api-key, anthropic-version e anthropic-beta de uma vez — cada formato reapresenta a mesma chave do seu jeito. O modelo decide o protocolo: claude-* vai messages, gpt-*/grok-*/muse-spark* vão responses, jev-* não é API de chat.
    metaAuthorization: Bearer — normaliza x-api-key encimaMantém anthropic-version e apaga só anthropic-beta, porque fala o protocolo Anthropic. Os 8 providers OpenAI-compat apagam os dois. E sobrescreve cache_control.ttl para 5m, único valor que o Meta aceita.
    mimoHeader próprio api-key — não é Bearer nem x-api-keyMonta o objeto de headers do zero e descarta os headers customizados do operador que conflitem (authorization, api-key, x-api-key, content-type, anthropic-version, anthropic-beta). Também escreve thinking sempre explícito e renomeia max_tokens para max_completion_tokens.
    z-aix-api-key no mirror; sempre Bearer no compatcache_control é implícito no upstream: o campo é removido de system[], tools[] e messages[].content[] em toda superfície que não seja o mirror /api/anthropic, para não sujar o log de auditoria dele.
    github-copilotauthorization: Bearer <copilotToken> + Copilot-Integration-IdRenova sozinho: se o token não existe ou está a menos de 60 s de expirar, busca outro, persiste em metadata.copilotToken e sobrescreve ctx.keyDetails.headers inteiro. Sem o Copilot-Integration-Id o Copilot não responde.
    minimaxx-api-key + anthropic-versionÉ destreamForCache: um pedido com stream: true vai sem stream ao upstream e o proxy re-sintetiza o SSE, porque o MiniMax só honra o cache de prompt no modo sem stream — em streaming ele cobra a entrada inteira.
    qwenx-api-key convertido para BearerTem um stripper próprio que roda antes do conversor, porque o anthropicToOpenAI pode vazar restos Anthropic-only como top_k e thinking.
    openrouterx-api-key convertido para BearerClaude sai por /v1/messages nativo, os demais modelos por /v1/chat/completions — o provider escolhe pelo id do modelo, não pela chave.
    O Codex recusa <code>stream: false</code> — e isso não é o LB ignorando você

    O endpoint de assinatura do ChatGPT não aceita requisição sem streaming. O provider força stream: true sempre; se você pediu não-streaming, o transformResponse rebufferiza a transcrição inteira numa mensagem Anthropic antes de responder. O resultado é correto, mas o custo de memória é o do stream completo.

    Formatos e conversão#

    A conversão só acontece quando o destino não fala Anthropic nativo. Quem decide é o próprio adapter, devolvendo postProcessShape no transformRequest: false significa que o upstream responde no formato Anthropic, e o corpo vai intacto com a resposta crua; true significa que o LB reconverte a resposta. O handler do proxy só bifurca nesse campo.

    Os conversores vivem em backend/src/converters/, e o barrel re-exporta só a fachada format.js:

    ConversorDireçãoQuem usa
    anthropicToOpenAIAnthropic → OpenAI Chato mais usado: 9 providers
    anthropicToResponsesAnthropic → ResponsesCodex, OpenCode no protocolo responses
    anthropicToGeminiNativeAnthropic → :generateContentGemini nativo, OpenCode no protocolo gemini
    geminiToAnthropicGemini → Anthropic (inclui o transform de stream)Gemini nativo, OpenCode
    responsesToAnthropic + responsesStreamTransformResponses → Anthropic (JSON e SSE)Codex, OpenCode
    openAIToAnthropicOpenAI Chat → AnthropicGemini na superfície compat, OpenCode
    mistralToAnthropicresposta da Mistral → AnthropicMistral

    Estes providers não convertem nada, porque falam Anthropic nativo nas duas direções:

  • anthropic
  • meta
  • minimax na superfície /anthropic
  • agentrouter
  • z-ai no mirror Anthropic
  • Conversor não é sinônimo de perda zero

    O anthropicToOpenAI pode deixar passar restos Anthropic que o upstream OpenAI não entende — top_k, thinking. Por isso o Qwen tem um stripper próprio, que roda antes do conversor; a filterOpenAIChat é a whitelist genérica compartilhada pelos outros. O opencode faz o contrário: detecta a resposta pelo shape do JSON (output, candidates, choices), não pelo protocolo declarado.

    Antes de qualquer conversão, sanitizeRequestBody normaliza o corpo inteiro: preenche metadata.user_id quando falta, corta códigos ANSI e sufixos [...] que o Claude Code injeta no nome do modelo, remove context_management e output_config por padrão, poda mídia antiga até caber no orçamento e valida cada bloco contra o allowlist de 17 tipos. A camada de conversão herda esse corpo já limpo.

    Contas de plano#

    Falar o mesmo protocolo não faz de um provedor uma chave de API comum. As contas de plano são assinaturas com janela de uso, e elas saem do rodízio antes do cooldown. São quatro famílias:

    PlanoComo a conta é reconhecidaDe onde vem o dado da janelaIdentidade da conta
    Claudeoauth_max, oauth_console, claude_cloudanthropic-ratelimit-unified-5h/7d-* na respostamesma organização Claude → mesma janela
    Codexcodex_oauthx-codex-primary/secondary-* na respostamesmo chatgpt-account-id → mesma janela
    MiniMaxprovider minimaxprobe em token_plan/remains, a cada 5 mincada chave é a própria conta
    Musemeta_deviceprobe em subs_usage, a cada 5 mincada chave é a própria conta

    A chave do limite é a conta upstream, não a chave. Duas chaves da mesma conta Codex, com o mesmo chatgpt-account-id, dividem a janela; o mesmo vale para duas chaves da mesma organização Claude. Sem identificador de conta, cada chave é a sua própria conta — e é por isso que o MiniMax e a Muse aparecem por provider: eles não carregam esse identificador.

    O Anthropic e o Codex publicam o estado em headers na resposta, de graça, a cada chamada. O MiniMax e a Muse não publicam: o LB faz uma consulta ativa a cada 5 minutos contra token_plan/remains e subs_usage, e outra logo depois de um 429. Quando a leitura passa do corte — 95% por padrão, ajustável por chave — a conta sai do rodízio até o reset; se todas as contas do grupo estouraram, o LB responde 429 imediato com o Retry-After real, sem tocar no upstream.

    A página de limites de uso detalha o corte, o estado no Redis e como uma leitura abaixo do corte devolve a conta ao rodízio.

    Próximos passos#

    Agora que você sabe o que cada provedor faz, veja como o LB escolhe entre eles.

    Depois de dominar o básico, explore além:

    Documentação gerada com a skill docs-padrao.