Skip to content

fix(ia): o resguardo de provider agora existe de verdade - #38

Merged
BarryBits merged 1 commit into
mainfrom
fix/ia-resguardo-provider
Jul 31, 2026
Merged

fix(ia): o resguardo de provider agora existe de verdade#38
BarryBits merged 1 commit into
mainfrom
fix/ia-resguardo-provider

Conversation

@BarryBits

Copy link
Copy Markdown
Owner

O erro real

"Gerar nova versão" falhou com "IA retornou narrativas inválidas: resposta vazia da IA [modelo=gpt-5-nano]". A IA não devolveu vazio — a OpenAI recusou por cobrança. A telemetria (ai_calls) tinha o erro guardado:

openai_http_429: "You exceeded your current quota, please check your plan and billing"

Padrão dos últimos 30 dias — toda chamada ao gpt-5-nano falhou; toda ao gemini-2.5-flash-lite passou:

data caller provider/modelo resultado
27/07 19:20 retrato_narrativa openai/gpt-5-nano ❌ 429, retries 3
27/07 14:56 retrato_narrativa gemini/gemini-2.5-flash-lite ✅ valid, 0 retry
23/07 10:45 retrato_narrativa openai/gpt-5-nano ❌ 429, retries 3
22/07 20:20 retrato_narrativa gemini/gemini-2.5-flash-lite ✅ valid, 0 retry
21/07 21:35 retrato_narrativa openai/gpt-5-nano ❌ 429, retries 3

E fallback_used NULL em todas as falhas: o resguardo nunca entrou.

Três defeitos empilhados

1. Fallback desligado por padrão. getFallbackProvider devolvia null a menos que AI_FALLBACK_PROVIDER estivesse setada à mão. Ninguém setou. Agora o padrão é ligado — o outro provider assume, desde que tenha chave. AI_FALLBACK_PROVIDER=off desliga.

2. O override de modelo vazava entre providers — este é o que tornava o resguardo decorativo. O caller passa model: "gpt-5-nano", a OpenAI estoura a quota, o cliente cai no Gemini… e chama o Gemini pedindo "gpt-5-nano". Mesmo com a env setada, o fallback morria na porta. resolveModel agora ignora override que pertence claramente ao outro provider.

3. Dois defaults contraditórios. getProvider() resolve gemini sem AI_PROVIDER; os callers faziam (process.env.AI_PROVIDER ?? "openai") por conta própria. Sem a env, o caller mandava modelo da OpenAI e o cliente chamava o Gemini com ele — ou seja, "só remover a variável" teria trocado um erro por outro.

A mensagem também mentia

meta.error carregava o 429; a mensagem só mostrava modelo/latência/retries. Agora provider, fallback e causa crua entram no texto. Mesma correção no retrato/narrativa, que respondia só "IA indisponível".

Validação

151 arquivos, 1403/1403 (13 novos) · tsc limpo · eslint 0 · zero migration.

Nada aqui muda qual provider é o primário — isso segue em AI_PROVIDER. O que muda é que a queda de um lado deixa de ser queda do sistema.

🤖 Generated with Claude Code

"Gerar nova versão" do Plano de Voo falhou com "IA retornou narrativas
inválidas: resposta vazia da IA [modelo=gpt-5-nano]". A IA não devolveu vazio —
a OpenAI recusou por cobrança. A telemetria da casa (`ai_calls`) tinha o erro
cru guardado o tempo todo:

  openai_http_429: "You exceeded your current quota, please check your plan
  and billing"

Padrão nos últimos 30 dias: TODA chamada que foi pro gpt-5-nano falhou assim
(15/07, 21/07 ×2, 23/07, 27/07); TODA que foi pro gemini-2.5-flash-lite passou
com valid=true e zero retry — inclusive as 5 gerações de Plano de Voo que estão
no ar. E `fallback_used` NULL em todas as falhas: o resguardo nunca entrou.

## Por que o resguardo não entrou — e não entraria

Três defeitos empilhados, cada um suficiente pra derrubar a geração:

1. FALLBACK DESLIGADO POR PADRÃO. `getFallbackProvider` devolvia null a menos
   que `AI_FALLBACK_PROVIDER` estivesse setada à mão. Ninguém setou. Com o
   Gemini ao lado, funcionando e de graça, um 429 de cobrança derrubava tudo.
   Agora o padrão é LIGADO: o outro provider assume, desde que tenha chave.
   `AI_FALLBACK_PROVIDER=off` desliga de propósito.

2. O OVERRIDE DE MODELO VAZAVA ENTRE PROVIDERS — e este é o que tornava o
   resguardo decorativo. O caller passa `model: "gpt-5-nano"`; a OpenAI estoura
   a quota; o cliente cai no Gemini... e chama o Gemini pedindo "gpt-5-nano",
   que não existe. Mesmo com a env setada, o fallback morria na porta.
   `resolveModel` agora ignora override que pertence claramente ao OUTRO
   provider (nome desconhecido — fine-tune, alias — segue respeitado).

3. DOIS DEFAULTS CONTRADITÓRIOS. `getProvider()` resolve gemini quando
   `AI_PROVIDER` está ausente; os callers do Plano de Voo e do Retrato faziam
   `(process.env.AI_PROVIDER ?? "openai")` por conta própria só pra decidir o
   override. Efeito: sem a env, o caller mandava nome de modelo da OpenAI e o
   cliente chamava o Gemini com ele. Ou seja, "só remover a variável" teria
   trocado um erro por outro. Os callers agora sempre passam o override da
   família OpenAI e deixam o cliente decidir — com (2) no lugar, é seguro.

## A mensagem também mentia

`meta.error` carregava o 429 e a mensagem montada só mostrava modelo/latência/
retries. Quem depurava ia atrás do prompt em vez da conta. Agora provider,
fallback e a causa crua entram no texto. Mesma correção no `retrato/narrativa`,
que respondia só "IA indisponível".

Nada aqui muda qual provider é o primário: isso segue em `AI_PROVIDER` (e o
default do cliente, gemini, é o plano grátis). O que muda é que a queda de um
lado deixa de ser queda do sistema.

Validação: 151 arquivos, 1403/1403 (13 novos cobrindo default, fallback com/sem
chave, desligamento explícito, o não-vazamento de modelo nos dois sentidos e o
caminho exato do bug) · tsc limpo · eslint 0 · zero migration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
atr-os Ready Ready Preview, Comment Jul 29, 2026 7:10pm

@supabase

supabase Bot commented Jul 29, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project tdlxqqgechxhkygdmsxq because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@BarryBits
BarryBits merged commit 24b9967 into main Jul 31, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant