fix(ia): o erro para de apontar pro provider errado - #44
Merged
Conversation
Caso real (Vertimetal, 2026-08-05). A geracao do Plano de Voo falhou e a tela mostrou: provider=gemini, modelo=gemini-2.5-flash-lite, retries 6, fallback=openai — causa: openai_http_429: "You have no credits remaining" A causa exibida era do FALLBACK. O Gemini havia falhado antes, e o motivo dele foi apagado — mandando a depuracao pra fatura da OpenAI enquanto o bloqueio real estava no Gemini, invisivel. Custou uma rodada inteira de investigacao. (A causa raiz era ambiental: variavel de ambiente na Vercel so entra em deployment criado DEPOIS dela; o deployment antigo nunca viu a GEMINI_API_KEY.) Duas mentiras no relatorio de erro: 1. `attempt.error = fbAttempt.error ?? attempt.error` SOBRESCREVIA a causa do primario. Agora as duas sobrevivem, nomeadas: `gemini: <causa> | fallback(openai): <causa>`. O primario e quem deveria ter respondido — a causa dele e a que importa. 2. O retorno de falha do `tryProvider` usava sempre `RETRY_DELAYS_MS.length`, entao um erro NAO-retriable (chave invalida, modelo inexistente) — que sai na PRIMEIRA tentativa — aparecia como "retries 3". Isso faz configuracao quebrada parecer instabilidade passageira e apaga o sinal de que nao adianta tentar de novo. Agora conta as tentativas reais. Zero mudanca de comportamento: nenhuma chamada nova, nenhum retry a mais, mesmo resultado em caso de sucesso. Muda so o que o erro conta. Os 3 testes novos mockam o SDK do Gemini e o fetch da OpenAI pra falhar juntos; 2 deles falham contra o codigo antigo (conferido com stash), o terceiro trava o caminho em que o fallback responde. Gate: 1495/1495 testes, tsc limpo, 0 erro/warning novo de eslint. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
This pull request has been ignored for the connected project Preview Branches by Supabase. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Nasceu de um caso real: a geração do Plano de Voo da Vertímetal falhou e o erro mandou a investigação pro lugar errado.
O que a tela mostrou
Lê-se como problema de fatura da OpenAI. Não era.
provider=geminié o primário — a causa exibida era do fallback. O Gemini falhou primeiro, e o motivo dele foi apagado antes de chegar na tela.A causa raiz era ambiental (variável de ambiente na Vercel só entra em deployment criado depois dela — o deployment antigo nunca viu a
GEMINI_API_KEY), mas o erro escondeu isso e custou uma rodada inteira de investigação.As duas mentiras
1. A causa do primário era sobrescrita —
ai-client.ts:Agora as duas sobrevivem, nomeadas:
gemini: <causa> | fallback(openai): <causa>. O primário é quem deveria ter respondido — a causa dele é a que importa.2. O contador de retries era fixo. O retorno de falha do
tryProviderusava sempreRETRY_DELAYS_MS.length, então um erro não-retriable (chave inválida, modelo inexistente) — que sai na primeira tentativa — aparecia comoretries 3. Isso faz configuração quebrada parecer instabilidade passageira, e apaga o sinal de que não adianta tentar de novo. Agora conta as tentativas reais.Risco
Zero mudança de comportamento: nenhuma chamada nova, nenhum retry a mais, mesmo resultado em caso de sucesso. Muda só o que o erro conta quando os dois providers caem.
Prova
Os 3 testes novos mockam o SDK do Gemini e o
fetchda OpenAI pra falharem juntos. 2 deles falham contra o código antigo (conferido rodando a suíte nova contra oai-client.tsdo HEAD); o terceiro trava o caminho em que o fallback responde — se todos passassem nos dois lados, não provariam nada.Verificação em produção
O caminho está saudável hoje, conferido no banco depois da geração de 03:13:
Gemini respondeu de primeira, sem tocar na OpenAI. Este PR é rede para a próxima vez.
Gate
1495/1495 testes (+3) · tsc limpo · 0 erro/warning novo de eslint.
🤖 Generated with Claude Code