whisper-money/config
Víctor Falcón 4038e60fbc
fix(ai): handle transient AI provider overloads — stop the Sentry noise and retry the dropped work (#595)
## Why

Sentry issue **PHP-LARAVEL-3S** (`ProviderOverloadedException: AI
provider [gemini] is overloaded`) recurs whenever Gemini returns 503/429
under high demand — 7 events in 3h during the last surge, 0 users
impacted.

Two problems behind it:

1. **Sentry noise.** `CategorizeTransactions::resolveChunkWithRetry`
retries, `laravel/ai` fails over providers, and a still-failing chunk is
deliberately dropped so the rest of the backfill proceeds. But the catch
reported **every** dropped chunk via `report()`, so an expected,
self-healing transient condition floods Sentry and buries real bugs.
2. **Silently lost work.** A dropped chunk leaves those transactions
uncategorized (`category_id NULL`) with nothing to re-trigger them: the
backfill jobs are one-shot (`tries = 1`), there is no scheduled
backfill, and the real-time listener only handles *new* transactions.
They stay uncategorized until someone manually re-runs a backfill.

## What

**1. Stop reporting transient failures.** Catch `FailoverableException`
(the marker interface for `ProviderOverloadedException` /
`RateLimitedException`) separately and log a warning instead of
reporting it. Everything else still goes to `report()` unchanged, so
real failures (malformed responses, insufficient credits, …) keep
surfacing.

**2. Retry the dropped work.** On a transient failure, schedule a
deferred, per-user `RetryTransientAiCategorizationJob` that re-reads the
user's still-pending transactions once the provider has had time to
recover (`ai_categorization.retry_delay`, default 10 min).
- `ShouldBeUnique` per user collapses a surge of dropped chunks into a
**single** retry.
- The unique lock is held through processing, so a retry that overloads
again **cannot chain another** — exactly one deferred attempt per
failure, no infinite loop. Failed 503s aren't billed.
- Wired in `resolve()`, so it covers every entry point (backfill,
onboarding, real-time listener, admin command).
- Model cost is negligible (per config), so the retry re-runs the
existing backfill rather than tracking which exact chunks failed.

## Tests

- Transient overload → chunk dropped, nothing reported, retry scheduled
for the user.
- Unexpected failure → reported, **no** retry scheduled.
- Retry job → categorizes still-pending transactions for a consenting
user; no-op without consent (agent never prompted).

Full `tests/Feature/Ai` + listeners suite green (111 tests); Pint clean.

## Existing prod backlog

The transactions already dropped before this ships stay `pending`; an
`ai:categorize-backfill <user>` recovers them on demand.

Fixes PHP-LARAVEL-3S
2026-06-26 20:07:06 +02:00
..
ai_categorization.php fix(ai): handle transient AI provider overloads — stop the Sentry noise and retry the dropped work (#595) 2026-06-26 20:07:06 +02:00
ai_suggestions.php feat(ai): add weekly AI-suggestions cohort report (#530) 2026-06-13 23:23:34 +02:00
app.php feat(demo): gate demo account access behind a config flag (#580) 2026-06-22 11:01:27 +00:00
auth.php chore: upgrade Laravel 12 to 13 (#242) 2026-03-25 12:56:33 +00:00
cache.php chore: upgrade Laravel 12 to 13 (#242) 2026-03-25 12:56:33 +00:00
cashier.php feat(pricing): dynamic Stripe pricing with locale-aware formatting (#204) 2026-03-05 11:41:59 +00:00
currencies.php feat(currency): add RSD (Serbian Dinar) (#567) 2026-06-20 12:46:26 +02:00
database.php feat(console): add agent:db command for querying local and prod DB (#522) 2026-06-12 18:35:14 +02:00
filesystems.php Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00
fortify.php Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00
inertia.php Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00
landing.php feat(landing): add signed auth links (#312) 2026-04-21 08:28:59 +01:00
logging.php fix(logging): keep laravel.log writable across container UIDs (#451) 2026-05-29 15:10:50 +02:00
mail.php feat(mail): use AWS SES for email delivery (#422) 2026-05-25 10:59:57 +02:00
pennant.php Add Cashflow Analytics Feature (#49) 2026-01-05 13:06:50 +01:00
queue.php Reapply "swap horizon -> queue:work on mysql" 2025-12-30 07:22:18 +01:00
sentry.php fix(sentry): only report errors in production (#467) 2026-06-01 12:41:31 +02:00
services.php feat(ai): add weekly AI-suggestions cohort report (#530) 2026-06-13 23:23:34 +02:00
session.php Set up a fresh Laravel app 2025-11-07 12:01:36 +00:00
subscriptions.php feat(ai): suggest automation rules during onboarding (#523) 2026-06-13 22:51:15 +02:00