Provedores de LLM
O Aurum fala um único protocolo, Chat Completions compatível com OpenAI, e
adapta os detalhes de cada provedor por um catálogo de perfis em YAML
(internal/llm/provider/profiles/profiles.yml, embutido no binário). Cada
perfil diz só o que muda no fio:
| Campo | O que define | Valores |
|---|---|---|
base_url |
URL base padrão; {VAR} vem da variável de ambiente VAR, {VAR:-padrão} usa o padrão quando ela está vazia |
URL |
key_env |
variáveis que guardam a chave, na ordem; vale a primeira não vazia | lista |
auth |
como a chave viaja | bearer, header (com auth_header), none |
path |
caminho anexado à URL base | /chat/completions |
query |
parâmetros de query (aceitam {VAR}) |
mapa |
token_field |
campo do limite de resposta | max_tokens, max_completion_tokens |
structured_output |
saída estruturada que o endpoint respeita | json_schema, json_object, none |
Como escolher o provedor
| Variável | Papel |
|---|---|
LLM_PROVIDER |
nome do perfil. Ausente: o comportamento de sempre (LLM_API_KEY + LLM_BASE_URL, requisição idêntica à de antes). |
LLM_BASE_URL |
sobrescreve a URL do perfil. Obrigatória só para openai-compatible. |
LLM_API_KEY |
a chave; cada perfil também aceita a variável nativa do provedor (veja abaixo). |
LLM_MODEL |
o modelo, como o provedor o nomeia. |
LLM_PROVIDERS_FILE |
arquivo YAML do operador, no mesmo formato, que acrescenta perfis ou substitui um perfil inteiro pelo nome. |
Regras que valem para todos:
- Perfil desconhecido falha (exit 1) listando os válidos; perfil sem chave, sem
a variável de um
{VAR}ou sem URL falha nomeando o que falta. Nunca há recaída silenciosa para outro provedor nem para "sem modelo": só as reservas que o operador declarou, e cada troca aparece no log. - O provedor, a URL e a chave vêm só do ambiente do operador, nunca da configuração do repositório revisado: um repositório não pode apontar a chave e o diff para um host dele.
- Toda resposta é conferida contra o contrato da revisão. Uma resposta sem a
lista de achados (
issues) deixa a revisão inconclusiva (exit 1,could not understand the model's response (validation_failed)), nunca aprovada. Isso importa para provedores sem saída estruturada. - Um erro do provedor chega resumido à mensagem do próprio provedor (envelopes
OpenAI
{"error":{...}}, Anthropic{"type":"error","error":{...}}, Google{"error":{"status",...}}ou[{"error":...}], Bedrock{"message":...}), com o nome do perfil e redigido (a chave e segredos conhecidos viram marcador):anthropic API error (status 401): authentication_error: ....
No workflow reutilizável do Aurum (.github/workflows/review.yml), o perfil
vem do input provider (vira LLM_PROVIDER no contêiner) e a chave do
secret LLM_API_KEY. No Actions o secret LLM_BASE_URL continua
obrigatório (falha fechada sem ele), mesmo com perfil: grave nele a URL do
perfil escolhido, a mesma da seção do provedor abaixo:
jobs:
review:
uses: Mpaape/AurumCode/.github/workflows/review.yml@v2.2.0
with:
provider: anthropic
model: <id do modelo no provedor>
secrets: inherit
Pela CLI, pela imagem e pelo servidor MCP, LLM_PROVIDER vale do mesmo jeito.
Tudo isto está demonstrado, com um provedor falso e local, no tutorial Provedores de LLM.
Provedor reserva (fallback)
Se o provedor principal falhar (fora do ar, limite de uso, erro 5xx, chave recusada, tempo esgotado), o Aurum repete a mesma requisição no próximo provedor da lista, em ordem, e segue com o primeiro que responder. Cada reserva é um provedor completo, de qualquer perfil do catálogo, declarada em slots numerados de 1 a 5:
| Variável | Papel |
|---|---|
LLM_FALLBACK_<n>_PROVIDER |
perfil da reserva n; vazio = openai-compatible |
LLM_FALLBACK_<n>_BASE_URL |
URL da reserva (no lugar de LLM_BASE_URL); opcional se o perfil tem URL padrão |
LLM_FALLBACK_<n>_API_KEY |
chave da reserva, a única lida para ela: LLM_API_KEY e as variáveis nativas (OPENAI_API_KEY, ANTHROPIC_API_KEY...) pertencem ao principal |
LLM_FALLBACK_<n>_MODEL |
modelo da reserva; o LLM_MODEL do principal não é herdado |
# principal: Bedrock; reserva 1: proxy LiteLLM interno; reserva 2: OpenAI
export LLM_PROVIDER=bedrock AWS_REGION=us-east-1 LLM_API_KEY=... LLM_MODEL=...
export LLM_FALLBACK_1_PROVIDER=litellm LLM_FALLBACK_1_BASE_URL=https://litellm.interno LLM_FALLBACK_1_API_KEY=...
export LLM_FALLBACK_2_PROVIDER=openai LLM_FALLBACK_2_API_KEY=... LLM_FALLBACK_2_MODEL=...
Regras:
- Só erro troca de provedor. Uma resposta que chegou mas não serve (fora do schema) deixa a revisão inconclusiva; o Aurum não pergunta a outro modelo até um concordar.
- Toda troca é anunciada no stderr, com o motivo:
aurumcode: provider litellm failed (...status 503...); trying fallback LLM_FALLBACK_1 (openai-compatible). - Todos caíram: a revisão falha fechada (exit 1), nomeando o erro de cada um, nunca aprovada.
- Reserva configurada pela metade falha antes de qualquer requisição,
nomeando o slot (
LLM_FALLBACK_1: ... set LLM_FALLBACK_1_API_KEY). A chave do principal nunca é enviada a uma reserva. - Tempo: cada provedor tem o seu limite (
AURUMCODE_LLM_TIMEOUT_SECONDS, 60 s por padrão); a cadeia inteira tem um limite por provedor, para que a reserva tenha tempo de responder depois de um principal que esgotou o dele. - Custo: o teto (
--limite) vale para a revisão inteira, qualquer que seja o provedor que respondeu; uma tentativa que falhou não consome orçamento. - Cache: uma revisão respondida por uma reserva não é guardada no cache de revisão, para não ser servida depois como se fosse do principal.
- Ferramentas da deliberação só vão para reservas que sabem chamar ferramentas.
- Como o principal, as reservas vêm só do ambiente do operador, nunca do repositório revisado.
No workflow reutilizável há duas reservas: inputs fallback_1_provider,
fallback_1_model, fallback_2_provider e fallback_2_model, e secrets
opcionais LLM_FALLBACK_1_API_KEY, LLM_FALLBACK_1_BASE_URL,
LLM_FALLBACK_2_API_KEY e LLM_FALLBACK_2_BASE_URL:
jobs:
review:
uses: Mpaape/AurumCode/.github/workflows/review.yml@v2.2.0
with:
provider: bedrock
fallback_1_provider: litellm
fallback_2_provider: openai
fallback_2_model: <id do modelo no provedor>
secrets:
LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
LLM_BASE_URL: ${{ secrets.LLM_BASE_URL }}
LLM_FALLBACK_1_API_KEY: ${{ secrets.LITELLM_KEY }}
LLM_FALLBACK_1_BASE_URL: ${{ secrets.LITELLM_URL }}
LLM_FALLBACK_2_API_KEY: ${{ secrets.OPENAI_KEY }}
No workflow reutilizável, o contêiner da revisão recebe do provedor só
LLM_API_KEY, LLM_BASE_URL, LLM_MODEL, LLM_PROVIDER e as variáveis das
reservas 1 e 2: AWS_REGION, AZURE_OPENAI_RESOURCE,
AZURE_OPENAI_DEPLOYMENT e AZURE_OPENAI_API_VERSION não chegam a ele. Por
isso, no Actions, uma reserva bedrock ou azure-openai exige
LLM_FALLBACK_<n>_BASE_URL com a URL completa, sem nenhum {VAR} a
preencher:
| Perfil da reserva | LLM_FALLBACK_<n>_BASE_URL |
|---|---|
bedrock |
https://bedrock-runtime.<região>.amazonaws.com/openai/v1 |
azure-openai |
https://<recurso>.openai.azure.com/openai/deployments/<deployment> |
Sem ela, a revisão falha antes de qualquer requisição, nomeando o slot e a
variável que falta (LLM_FALLBACK_1: ... set AWS_REGION). No azure-openai,
a api-version fica na padrão do perfil.
Na action (uses: Mpaape/AurumCode@...), as mesmas variáveis vão no env:
do passo. Demonstração com provedor falso: casos reserva-assume,
duas-reservas, falha-todas-caem e falha-reserva-sem-chave do tutorial
Provedores de LLM.
openai-compatible (padrão)
Qualquer endpoint compatível com OpenAI: é o comportamento de antes deste
catálogo. Bearer, max_tokens, response_format json_schema/json_object.
export LLM_PROVIDER=openai-compatible # ou deixe LLM_PROVIDER vazio
export LLM_BASE_URL=http://localhost:8080/v1
export LLM_API_KEY=...
OpenAI
https://api.openai.com/v1, Bearer. Usa max_completion_tokens, o único limite
que os modelos de raciocínio aceitam no Chat Completions
(guia de raciocínio).
export LLM_PROVIDER=openai
export OPENAI_API_KEY=... # ou LLM_API_KEY
export LLM_MODEL=... # o identificador do modelo no provedor
Azure OpenAI
Rota por deployment com api-version na query e a chave no header api-key
(nunca Authorization), conforme a
referência da API.
A versão padrão é a GA 2024-10-21; troque por AZURE_OPENAI_API_VERSION.
Usa max_completion_tokens, exigido pelos modelos de raciocínio
(how-to).
export LLM_PROVIDER=azure-openai
export AZURE_OPENAI_RESOURCE=meu-recurso
export AZURE_OPENAI_DEPLOYMENT=meu-deployment
export AZURE_OPENAI_API_KEY=... # ou LLM_API_KEY
Limitação: a API v1 nova (/openai/v1/, sem api-version) também funciona,
apontando LLM_BASE_URL=https://meu-recurso.openai.azure.com/openai/v1 com
LLM_PROVIDER=openai-compatible se o recurso aceitar Bearer, ou com um perfil
próprio em LLM_PROVIDERS_FILE (header api-key, sem query).
Anthropic
A camada de compatibilidade com o OpenAI SDK
em https://api.anthropic.com/v1, Bearer. Ela ignora response_format: o
perfil não o envia (structured_output: none) e a resposta é conferida contra
o contrato da revisão; fora dele, a revisão fica inconclusiva.
export LLM_PROVIDER=anthropic
export ANTHROPIC_API_KEY=... # ou LLM_API_KEY
export LLM_MODEL=... # o identificador do modelo no provedor
Google (Gemini)
O endpoint compatível com OpenAI
em https://generativelanguage.googleapis.com/v1beta/openai, Bearer com a chave
da Gemini API. O Gemini restringe o subconjunto de JSON Schema aceito; o perfil
pede só o modo json_object e a resposta é conferida localmente.
export LLM_PROVIDER=google
export GEMINI_API_KEY=... # ou LLM_API_KEY
export LLM_MODEL=... # o identificador do modelo no provedor
Amazon Bedrock
Com chave de API do Bedrock: o
endpoint Chat Completions do bedrock-runtime
em https://bedrock-runtime.{AWS_REGION}.amazonaws.com/openai/v1, com a chave
como Bearer (chaves de API).
export LLM_PROVIDER=bedrock
export AWS_REGION=us-east-1
export AWS_BEARER_TOKEN_BEDROCK=... # ou LLM_API_KEY
export LLM_MODEL=... # o identificador do modelo no provedor
Sem chave de API (só credenciais IAM): o Aurum não assina SigV4. Use o
proxy LiteLLM na frente do Bedrock
(provedor Bedrock do LiteLLM)
e aponte o perfil litellm para ele:
# litellm-config.yaml
model_list:
- model_name: bedrock-claude
litellm_params:
model: bedrock/<id do modelo no provedor>
aws_access_key_id: os.environ/AWS_ACCESS_KEY_ID
aws_secret_access_key: os.environ/AWS_SECRET_ACCESS_KEY
aws_region_name: os.environ/AWS_REGION_NAME
docker run --rm -p 4000:4000 \
-v "$PWD/litellm-config.yaml:/app/config.yaml:ro" \
-e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_REGION_NAME \
-e LITELLM_MASTER_KEY=sk-local-master \
docker.litellm.ai/berriai/litellm:latest --config /app/config.yaml
export LLM_PROVIDER=litellm LITELLM_API_KEY=sk-local-master LLM_MODEL=bedrock-claude
Fixe a imagem do LiteLLM por digest em uso real.
LiteLLM
O proxy LiteLLM em
http://localhost:4000, Bearer com a chave mestra ou virtual. Serve como
porta única para qualquer provedor que o LiteLLM conheça.
export LLM_PROVIDER=litellm
export LITELLM_API_KEY=sk-... # ou LLM_API_KEY
export LLM_BASE_URL=http://litellm.example.com:4000 # se não for localhost
OpenRouter
https://openrouter.ai/api/v1, Bearer
(visão geral da API). A
saída json_schema depende do modelo e do provedor por trás
(structured outputs),
então o perfil pede json_object e confere a resposta localmente.
export LLM_PROVIDER=openrouter
export OPENROUTER_API_KEY=... # ou LLM_API_KEY
export LLM_MODEL=... # o identificador do modelo no provedor
OpenCode Zen
O gateway OpenCode Zen em
https://opencode.ai/zen/v1, Bearer, para os modelos servidos em
/chat/completions.
export LLM_PROVIDER=opencode
export OPENCODE_API_KEY=... # ou LLM_API_KEY
export LLM_MODEL=... # o identificador do modelo no provedor
O servidor MCP do Aurum no OpenCode
O OpenCode também é cliente MCP, ao lado do Claude Code, do Codex e do
Cursor (veja Aurum no agente de código). O servidor MCP do Aurum
(aurumcode mcp) entra no opencode.json como servidor local, no formato da
documentação de servidores MCP do OpenCode;
as variáveis do provedor vêm do ambiente pela substituição {env:...} da
configuração do OpenCode, sem a chave no
arquivo:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"aurum": {
"type": "local",
"command": ["aurumcode", "mcp"],
"enabled": true,
"environment": {
"LLM_PROVIDER": "{env:LLM_PROVIDER}",
"LLM_API_KEY": "{env:LLM_API_KEY}"
}
}
}
}
Ollama
O endpoint compatível com OpenAI
do Ollama em http://localhost:11434/v1. O servidor ignora a chave: o perfil
não envia credencial (auth: none) e, pela CLI e pela imagem, não exige
LLM_API_KEY. No workflow reutilizável, o secret LLM_API_KEY precisa ter
algum valor mesmo para o Ollama ou outro endpoint sem autenticação: o
workflow recusa secret vazio antes da revisão (passo Check provider
configuration). Qualquer texto serve, porque o perfil não o envia.
export LLM_PROVIDER=ollama
export LLM_MODEL=... # o identificador do modelo no provedor
Outro provedor
Qualquer endpoint compatível com OpenAI que não esteja no catálogo entra por
LLM_PROVIDERS_FILE, sem mudar código:
profiles:
meu-gateway:
description: gateway que autentica por header próprio
base_url: http://localhost:9000/v1
key_env: [GATEWAY_KEY]
auth: header
auth_header: x-gw-key
path: /chat/completions
token_field: max_completion_tokens
structured_output: none
export LLM_PROVIDERS_FILE=$PWD/meus-perfis.yml LLM_PROVIDER=meu-gateway GATEWAY_KEY=...
Um valor inválido (auth, token_field, structured_output fora da lista,
path sem /) falha ao carregar o arquivo, nunca na hora de chamar.
Verificação ao vivo
O workflow manual .github/workflows/providers-smoke.yml (workflow_dispatch)
roda uma revisão mínima por provedor cujos secrets existam no repositório
e registra os demais como "não verificado" (job pulado, nunca verde; sem nenhum secret a execução falha). Os testes
de contrato de cada dialeto rodam em todo PR, sem rede.
| Provedor | Secrets do smoke |
|---|---|
| openai | OPENAI_API_KEY |
| azure-openai | AZURE_OPENAI_API_KEY, AZURE_OPENAI_RESOURCE, AZURE_OPENAI_DEPLOYMENT |
| anthropic | ANTHROPIC_API_KEY |
GEMINI_API_KEY |
|
| bedrock | AWS_BEARER_TOKEN_BEDROCK, AWS_REGION |
| openrouter | OPENROUTER_API_KEY |
| opencode | OPENCODE_API_KEY |
| litellm | LITELLM_API_KEY, LITELLM_BASE_URL |
O modelo de cada smoke vem da variável de Actions SMOKE_MODEL_<PROVEDOR>
(por exemplo SMOKE_MODEL_OPENAI). Ollama só é verificável numa máquina com o
servidor local.